从0到1构建计算机(11/12)--在Hack上运行小游戏

经过了前面10章漫长的旅程,我们终于要达成我们的目标了:在一个完全由自己实现的计算机平台上运行一个小游戏。这个小游戏要经过编写、编译、转换虚拟机字节码、汇编、生成机器代码等步骤,最终运行在自己搭建的虚拟计算机上。在游戏的运行过程中,每一条指令都是我们亲手编写,每一个细节我们都了如指掌,是我们完完全全地创造了它(虽然需要基于一个硬件虚拟机),这确实是一件非常有意思的事情。

骑手快跑

我这里编写了一个非常简单的小游戏:骑手快跑。在屏幕上会位置随机地出现食物房子,我们扮演骑手,通过上下左右键控制骑手的行动。骑手需要先取到食物装到车上,然后运送到房子处并领取奖励金,完成配送后骑手又可以继续配送下一单,当奖励金达到一定数额后游戏通关🙄。

骑手取货中
骑手取到货
骑手配送中
配送下一单
奖励金达标,通关

实现

main() 先创建游戏实例,然后运行游戏,游戏结束后调用 dispose() 回收内存

class Main {

    function void main() {
        var RunnerGame game;
        do RunnerGame.newInstance();
        let game = RunnerGame.getInstance();

        do game.run();
        do game.dispose();

        return;
    }
}

RunnerGame类是游戏的核心控制类。负责创建骑手、食物、房子等对象;然后监听用户键盘输入、控制游戏逻辑、调整游戏状态、控制屏幕刷新。

class RunnerGame {

    static RunnerGame instance;    // 游戏单例

    field  Runner     runner;             // 骑手
    field  Food       food;               // 食物
    field  House      house;              // 配送地点
    
    field  int        runnerX, runnerY;   // 骑手当前位置
    field  int        foodX, foodY;       // 食物当前位置
    field  int        houseX, houseY;     // 房屋当前位置
    field  boolean    picked;             // 是否取到货
    field  int        score;              // 挣到的钱

    field  boolean    exit;               // 游戏结束

    constructor RunnerGame new() {
	    do Screen.clearScreen();

	    let runnerX = 230;
	    let runnerY = 120;
	    let foodX = 100;
	    let foodY = 200;
	    let houseX = 400;
	    let houseY = 50;

	    let runner = Runner.new(runnerX, runnerY);
	    let house = House.new(houseX, houseY);
	    let food = Food.new(foodX, foodY);

	    let picked = false;
	    let score = 0;
	    let exit = false;

	    // 底部分数分数记录区
        do Screen.drawRectangle(0, 238, 511, 240);
	    do Output.moveCursor(22,0);
	    do Output.printString("money: 0");
	
        return this;
    }

    method void dispose() {
        do runner.dispose();
	    do food.dispose();
	    do house.dispose();
        do Memory.deAlloc(this);

        return;
    }

    function void newInstance() {
        let instance = RunnerGame.new();
        return;
    }
    
    function RunnerGame getInstance() {
        return instance;
    }

    // 开始游戏。用键盘控制骑手位置,先取货,再送货,然后获得奖励
    method void run() {
        var char key;
        while (~exit) {
            // 等待按下按键
            while ((key = 0) & (~exit)) {
                let key = Keyboard.keyPressed();
                do Sys.wait(50);
            }

            do moveRunner(key);

            // 等待按键松开
            while ((~(key = 0)) & (~exit)) {
                let key = Keyboard.keyPressed();
                do Sys.wait(50);
            }

            do updateStatus();
            do reDraw();
        }

	    if (exit) {
            do Output.moveCursor(1,23);
            do Output.printString("Congratulations!");
	    }
            
        return;
    }

    method void moveRunner(int key) {
        // 左 = 130,上 = 131,右 = 132,下 = 133
        if (key = 130) { 
        	let runnerX = runnerX - 20;
        } else {
            if (key = 131) { 
            	let runnerY = runnerY - 20;
            } else {
                if (key = 132) {
                	let runnerX = runnerX + 20;
                } else {
                	if (key = 133) {
                		let runnerY = runnerY + 20;
                	}
                }
	        }
        }

        // 防止越界
        if (runnerX > 490) {
        	let runnerX = 490;
        }
        if (runnerX < 0) {
        	let runnerX = 0;
        }

        if (runnerY > 236) {
        	let runnerY = 236;
        }
        if (runnerY < 0) {
        	let runnerY = 0;
        }

        do runner.moveTo(runnerX, runnerY);

        return;
    }

    method void updateStatus() {
    	// 取到食物
    	if ((Math.abs(foodX - runnerX) < 20) & (Math.abs(foodY - runnerY) < 20)) {
    		let picked = true;
    		do runner.setPicked(picked);
    	}

    	// 送到食物
    	if (picked & ((Math.abs(houseX - runnerX) < 20) & (Math.abs(houseY - runnerY) < 20))) {
    		let score = score + 30;
    		if (score > 100) {
    			let exit = true;
    		}

    		let picked = false;
    		do runner.setPicked(picked);

    		do newRound();
    	}

    	return;
    }

    // 完成一次配送,更新食物和房屋的位置
    method void newRound() {
    	// 更新位置
    	let foodX = mod(foodX + houseX , 490);
    	let foodY = mod(foodY + houseY , 236);
    	let houseX = mod(foodY + houseX , 490);
    	let houseY = mod(houseX + foodY , 236);

    	do food.moveTo(foodX, foodY);
    	do house.moveTo(houseX, houseY);

    	return;
    }

    // 重新绘制
    method void reDraw() {
    	do runner.show();
    	do house.show();
    	if (picked) {
            do food.hide();    		
    	} else {
            do food.show();   		
    	}

    	do Output.moveCursor(22,7);
        do Output.printInt(score);

        return;
    }

    // 补充一个取模的方法
    method int mod(int x, int y) {
        var int q;
        let q = Math.divide(x, y);
        return x - (q*y);
    }
}

Runner,House,Food类负责表示个字的对象,维护对象位置信息,状态信息,并负责绘制自己的图形。

class Runner {

    field int x, y;           // 骑手位置
    field int width, height;  // 骑手宽高 20*20
    field boolen picked;          // 是否已取货

    // 新创建一个骑手
    constructor Runner new(int Ax, int Ay) {
        let x = Ax;
        let y = Ay;
        let width = 20;
        let height = 20;
        let picked = false;

        do show();

        return this;
    }

    method void dispose() {
        do Memory.deAlloc(this);
        return;
    }

    // 在screen上展示骑手
    method void show() {
        do Screen.setColor(true);
        do draw();
        return;
    }

    // 抹掉当前在screen上展示的骑手
    method void hide() {
        do Screen.setColor(false);
        do draw();
        return;
    }

    /** 绘制房屋 */
    method void draw() {
        do Screen.drawRectangle(x, y + 9, x + 19, y + 10);
        do Screen.drawCircle(x + 5,  y + 15, 4);
        do Screen.drawCircle(x + 15, y + 15, 4);
        do Screen.drawLine(x + 15, y + 9, x + 15, y + 4);
        do Screen.drawLine(x + 11, y + 4, x + 19, y + 4);

        if (picked) {
            do Screen.drawCircle(x + 4, y + 4, 3);
        }

        return;
    }

    method void setPicked(boolean Apicked) {
        let picked = Apicked;
        return;
    }

    method void moveTo(int Ax, int Ay) {
        do hide();  //先抹掉当前骑手

        let x = Ax;
        let y = Ay;
        do draw();   // 重新画骑手

        return;
    }
}

class Food {

    field int x, y;           // 食物位置
    field int width, height;  // 食物宽高 15*15

    // 新创建一个食物
    constructor Food new(int Ax, int Ay) {
        let x = Ax;
        let y = Ay;
        let width = 15;
        let height = 15;

        do show();

        return this;
    }

    method void dispose() {
    	...
    }

    // 在screen上展示食物
    method void show() {
    	...
    }

    // 抹掉当前在screen上展示的食物
    method void hide() {
    	...
    }

    /** 绘制食物 */
    method void draw() {
    	...
    }

    method void moveTo(int Ax, int Ay) {
    	...
    }
}

class House {

    field int x, y;           // 房屋位置
    field int width, height;  // 房屋宽高 20*20

    // 新创建一个房屋
    constructor House new(int Ax, int Ay) {
        let x = Ax;
        let y = Ay;
        let width = 20;
        let height = 20;

        do show();

        return this;
    }

    method void dispose() {
    	...
    }

    // 在screen上展示房屋
    method void show() {
    	...
    }

    // 抹掉当前在screen上展示的房屋
    method void hide() {
    	...
    }

    /** 绘制房屋 */
    method void draw() {
    	...
    }

    method void moveTo(int Ax, int Ay) {
    	...
    }
}

广告

当美团骑手,做有为青年

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值