动画在游戏中扮演非常重要的角色,
当绘制的角色在萤幕上动起来时,就像是角色活起来一样。
卡比接下来想要跟大家分享这份喜悦!
Crab
在游戏里面在每次移动时都会把手举起来,感觉像在跟玩家打招呼呢!
所以我们需要再画一张把手举起来的图,并透过切换两张图达到打招呼的效果。
这类透过切换图还达成效果的动画称作 定格动画 (Frame Animation),
在很多领域都会应用的到喔!
首先我们要画 Crab
举手的图形
-- src/characters/Crab.ts
const image2 = [
[0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1],
[1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0],
];
稍微改一下我们的 Crab
函式,
export default function Crab() {
const graphics = new Graphics();
+ const image = image2;
for (let y = 0; y < image.length; y++) {
for (let x = 0; x < image[y].length; x++) {
if (image[y][x] === 0) continue;
graphics.beginFill(0xffffff);
graphics.drawRect(x, y, 1, 1);
graphics.endFill();
}
}
return graphics;
}
这时画面上应该长这样
当然,程序执行时,我们不可能透过手动更改程序码下去做更换图片,
所以我们需要先撰写好 什麽时机点要做什麽事 ,像这样的程序流程。
卡比接下来要示范,更换图形的方式。
以下方式只是众多换图方式的其中一种,大家可以尝试出不同的方式
现代动画大部分都是以一秒刷新60个画面的方式来播放,浏览器也是。
而卡比想在每次画面刷新前都去执行某段程序码,
这个在做动画中非常重要的函式,叫做 requestAnimationFrame。
透过这个函式,我们可以在画面更新之前执行某些运算,
来改变每次画面刷新的结果。
但是 requestAnimationFrame
是比较底层的 API,
实务上可能会需要更多的功能跟资料,例如. 距离上次刷新过了多久、能不能暂停 ...etc
而卡比接下来会使用的是 pixi.js
里面的 Ticker。
他已经帮我们准备好了一些常用的功能。
pixi.js
的 Application
预设会给一个 Ticker
,
所以我们只需要把 src/main.ts
加工一下,
-- src/main.ts
const app = new Application({
width: 20,
height: 20,
resolution: 10,
});
document.querySelector("#app")?.append(app.view);
app.stage.addChild(Crab());
+ app.ticker.add(() => {
+ });
你可以试试看在刚刚加入的 arrow function
里面执行 console.log
,
体验一秒执行60次的感觉。
接下来卡比要在每次刷新时,
重新绘制一个 Crab
并重新新增到画面上。
-- src/characters/Crab.ts
export default function Crab() {
return () => {
const graphics = new Graphics();
const image = image2;
for (let y = 0; y < image.length; y++) {
for (let x = 0; x < image[y].length; x++) {
if (image[y][x] === 0) continue;
graphics.beginFill(0xffffff);
graphics.drawRect(x, y, 1, 1);
graphics.endFill();
}
}
return graphics;
};
}
上面的写法叫做 Higher Order Function
,
就是 函式
可以回传出另一个 函式
。
然後在 src/main.ts
做出以下更动,
const app = new Application({
width: 20,
height: 20,
resolution: 10,
});
document.querySelector("#app")?.append(app.view);
- app.stage.addChild(Crab());
+ const update = Crab();
app.ticker.add(() => {
+ app.stage.removeChildren();
+ app.stage.addChild(update());
});
这样就可以在每次画面刷新前,将画面重置,重新运算产生结果。
然後,回到 src/characters/Crab.ts
-- src/characters/Crab.ts
export default function Crab() {
let current = 0;
const images = [image, image2];
return () => {
const image = images[current % images.length];
current += 1;
const graphics = new Graphics();
for (let y = 0; y < image.length; y++) {
for (let x = 0; x < image[y].length; x++) {
if (image[y][x] === 0) continue;
graphics.beginFill(0xffffff);
graphics.drawRect(x, y, 1, 1);
graphics.endFill();
}
}
return graphics;
};
}
这样画面上的 Crab
就会挥手了!但是...
好像挥的太快了,手会不会断掉。
因为现在每一次画面刷新都会执行运算,
实际上我们只需要一段时间执行一次而已,而其他的时间不执行运算,
Throttle
这个时候就派上用场了。
首先,我们需要获得 距离上次刷新过了多少时间
後面简称 Delta Time
。
-- src/main.ts
app.ticker.add(() => {
app.stage.removeChildren();
- app.stage.addChild(update());
+ app.stage.addChild(update(app.ticker.deltaMS));
});
透过 app.ticker.deltaMS
我们获得了 Delta Time
,
将它抛进 update
函式後,
-- src/characters/Crab.ts
export default function Crab() {
let current = 0;
const images = [image, image2];
+ let timePass = 0;
- return () => {
+ return (delta: number) => {
+ timePass += delta;
const image = images[current % images.length];
- current += 1;
+ if (timePass > 1000) {
+ current += 1;
+ timePass = 0;
+ }
const graphics = new Graphics();
for (let y = 0; y < image.length; y++) {
for (let x = 0; x < image[y].length; x++) {
if (image[y][x] === 0) continue;
graphics.beginFill(0xffffff);
graphics.drawRect(x, y, 1, 1);
graphics.endFill();
}
}
return graphics;
};
}
这样就只会每 1 秒 切换一次图片了!
最後结果如下:
以下提供其他角色的图片,大家可以自行实作看看!
const image1 = [
[0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0],
[0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1],
[1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1],
[0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0],
];
const image2 = [
[0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1],
[1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0],
];
const image1 = [
[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0],
[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
];
const image2 = [
[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0],
[0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0],
[0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0],
];
const image1 = [
[0, 0, 0, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 0],
[1, 1, 0, 1, 1, 0, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 1, 0, 0, 1, 0, 0],
[0, 1, 0, 1, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 1, 0, 1],
];
const image2 = [
[0, 0, 0, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 0],
[1, 1, 0, 1, 1, 0, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1],
[0, 1, 0, 1, 1, 0, 1, 0],
[1, 0, 0, 0, 0, 0, 0, 1],
[0, 1, 0, 0, 0, 0, 1, 0],
];
>>: 每个人都该学的30个Python技巧|技巧 4:字串格式化(字幕、衬乐、练习)
接续上一篇AlertDialog的基础介绍,今天来接着介绍更多AlertDialog的用法。 从上一...
Q_Q .. 对预设建立的 component 延伸自订样式 import styled from...
身为一个App的开发新手常常会遇到一些莫名其妙又难以解决的问题,直到找到问题答案才发现根本是自己愚蠢...
解读《征服》这本书,很有意思的是其中的时间管理,注意力管理,还有风险管理的维度展开。 把我们对魔术师...
前言 今天继续挑战top 100 liked中sum相关的题目─416. Partition Equ...