Drive 开车
代码
一样的引入global()
并且加载sprite,可以直接加载Aseprite导出的内容
复制 kaboom.global();
loadRoot("/pub/img/");
loadSprite("sky", "sky.png");
loadSprite("road", "road.png");
loadAseprite("car", "car.png", "car.json");
loadSprite("apple", "apple.png");
loadSprite("pineapple", "pineapple.png");
初始化窗口
复制 init({
width: 160,
height: 120,
scale: 4,
debug: true,
});
创建场景main首先创建不同的层级,并且layers(names, [default])
传入"game"作为默认层
复制 layers([
"bg",
"game",
"ui",
], "game");
创建变量
复制 const upBound = 40;
const lowBound = height() - 12;
const speed = 90;
let speedMod = 1;
制作滚动背景的方式 scroll backgrounds :
通过创建两张sprite(每一张都是屏幕的两倍宽度),然后创建action tag "road"
,在这里面加入一个移动的方法:移动速度为 speed * speedMod,并且判断,如果自身移动超过了屏幕左侧完全消失后再次出现在屏幕右侧。
复制 add([
sprite("road"),
pos(0, 0),
layer("bg"),
"road",
]);
add([
sprite("road"),
pos(width() * 2, 0),
layer("bg"),
"road",
]);
action("road", (r) => {
r.move(-speed * speedMod, 0);
if (r.pos.x <= -width() * 2) {
r.pos.x += width() * 4;
}
});
创建玩家角色
复制 const car = add([
sprite("car"),
pos(24, height() / 2),
color(),
origin("center"),
area(vec2(-12, -6), vec2(12, 8)),
{
speed: 100,
//这个地方因为每个component返回的是js obj,所以常规语法{key:value}
},
]);
播放角色(车)的动画,因为前面加载的Asprite的png的json文件中,包含了对于动画的定义
生成苹果🍎 和菠萝🍍 首先创建一个const来随机取出apple或者pineapple的字符,然后创建一个带有这个字符,对应的图像,还有action的tag "obj"
,出现在屏幕右边缘,在上下的边缘中间随机的位置。
定义的tag "obj"
主要是取让其想做运动,并且在超过屏幕范围之后销毁。
复制 loop(0.4, () => {
const obj = randl([
"apple",
"pineapple",
]);
add([
sprite(obj),
"obj",
obj,
pos(width(), rand(lowBound, upBound)),
]);
});
action("obj", (o) => {
o.move(-speed * speedMod, 0);
if (o.pos.x <= -width()) {
destroy(o);
}
});
然后处理碰撞事件:当car与带有"apple"
和"pineapple"
碰撞的时候,销毁对象并且增加快乐值
复制 // collision resolution
car.collides("apple", (a) => {
destroy(a);
happiness.value += 50;
});
car.collides("pineapple", (a) => {
destroy(a);
happiness.value += 100;
});
快乐值代表着一个简单的分数计算系统,首先创建一个hapiness
对象显示,在ui层显示,存放一个value
变量。并且创建一个action,当速度<1的时候,每帧分数-2,当速度>1的时候,每帧分数+1,并且让其text每帧更新数值。
复制 // happiness counter
const happiness = add([
text("0", 4),
pos(4, 4),
layer("ui"),
{
value: 0,
},
]);
happiness.action(() => {
if (speedMod < 1) {
happiness.value -= 2;
} else if (speedMod > 1) {
happiness.value += 1;
}
happiness.text = `happiness: ${happiness.value}`;
});
输入系统 Input System
复制 // input
keyDown("up", () => {
if (car.pos.y > upBound) {
car.move(0, -car.speed);
}
});
keyDown("down", () => {
if (car.pos.y < lowBound) {
car.move(0, car.speed);
}
});
keyDown("left", () => {
speedMod = 0.5;
car.animSpeed = 0.1 / speedMod;
});
keyDown("right", () => {
speedMod = 3;
car.animSpeed = 0.1 / speedMod;
});
keyRelease(["left", "right"], () => {
speedMod = 1;
car.animSpeed = 0.1 / speedMod;
});
其他
在代码下面还有另一个scene death
为结束页面,然后可以在上方例如吃到菠萝的时候让其进行跳转,值得注意的是这个scene("death", (score)=>{})
他接受一个 score
参数并显示这个 score
,如果是 go("death", 123)
他就会显示123
所以go() 的第二个即后面的参数就是传给这个scene的,相当于是不同scene之间的消息传递机制,没有定义的参数js默认就是undefined。
复制 ...
go("death",happiness.value);
...
scene("death", (score) => {
add([
text(score, 24),
]);
add([
text("press spacebar to play again", 5),
pos(0, -20),
]);
keyPress("space", () => {
//reload("main"); //这行删掉,比较早期的函数
go("main");
});
});
方法
randl([])
意思是在数组中随机,现在已经改为 choose(arr)
: get a random element from the array
复制 const obj = randl([
"apple",
"pineapple",
]);
关于绘制文字的方法 text(txt, size, [conf])
- conf 代表 config 更多配置,例如字体和宽度
复制 // note: this automatically gives the obj an 'area()' component
const obj = add([
// content, size
text("oh hi", 64),
]);
const obj = add([
text("oh hi", 64, {
width: 120, // wrap when exceeds this width (defaults to 0 no wrap)
font: "proggy", // font to use (defaults to "unscii")
}),
]);
// update the content
obj.text = "oh hi mark";
进行场景的切换 go(name, [...args])
switch to a scene,arguments,可以传入任意类型,直接把 go("death", .....) 后面的所有参数原封不动传到scene函数的参数里。
复制 // go to "paused" scene when pressed "p"
scene("main", () => {
let score = 0;
keyPress("p", () => {
go("gameover", score);
})
});
scene("gameover", (score) => {
// display score passed by scene "main"
add([
text(score),
]);
});