KaBoom-drive

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文件中,包含了对于动画的定义

car.play("move");

生成苹果🍎 和菠萝🍍 首先创建一个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),
    ]);
});

最后更新于