iT邦幫忙

2022 iThome 鐵人賽

DAY 22
0
Modern Web

拾起 Canvas,人人都是大藝術家!系列 第 22

第 22 幅 - 用 Canvas 復刻 Pokemon ,做個 RPG 小遊戲吧!(上)

  • 分享至 

  • xImage
  •  

最後一週了,我們來點進階的!一起進到童年的遊戲世界,試著利用 Canvas 做個 RPG 遊戲,相信很多人都有經歷過寶可夢遊戲的時代,每次看到畫面總會想起那時候的童年回憶,下面附上一張遊戲畫面來讓大家回憶一下:

https://ithelp.ithome.com.tw/upload/images/20221007/20130630wwyFoHtIwt.jpg
Image Source

這就是我們今天的目標, Canvas 技術就可以讓我們完成類似寶可夢的遊戲介面與簡單的操作行為。

  1. 初始 HTML 與 Canvas 畫布
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="canvas.js"></script>
    <title>珊迪 Canvas Game</title>
</head>
<body>
    <canvas id="canvas" width="500" height="500"></canvas>
</body>
</html>
  1. Canvas 初始地圖
window.onload = () => {
    // 初始Canvas與遊戲參數
    const canvas = document.getElementById("canvas");
    const ctx = canvas.getContext("2d");
    const {offsetWidth: CanvasWidth, offsetHeight: CanvasHeight}
           = document.getElementById("canvas");
    let gameMapLoad = false;

    // 初始地圖物件
    const gameMap = new Image();
    gameMap.onload = () => {
        gameMapLoad = true;
        if (gameMapLoad) assetsLoaded();
    }
    gameMap.src = "images/gamemap.jpg";
    
    // 初始事件
    function assetsLoaded() {
        ctx.drawImage(gameMap, 0, 0);
    }
}
  1. Canvas 初始人物
window.onload = () => {
    // 初始Canvas、遊戲相關參數
    ...
    let gameMapLoad = false, playerImageLoaded = false;
    let zoom = 20;
    let speed = 1;
    
    // 動作參數
    const moveConfig = {
        "LEFT": "left",
        "RIGHT": "right",
        "UP": "up",
        "DOWN": "down",
        "STAND": "stand",
        "DEFAULT": "default"
    }
    let moveMappingImage = {};
    for (const [, value] of Object.entries(moveConfig)) {
        moveMappingImage[value] = {
            [moveConfig.DEFAULT]: `${value}-1`,
            [moveConfig.STAND]: `${value}-1`,
            [`${value}-1`]: `${value}-2`,
            [`${value}-2`]: `${value}-1`,
        }
    }
    
    ...
    // 初始人物物件
    const playerImage = new Image();
    playerImage.onload = () => {
        playerImageLoaded = true;
        if (playerImageLoaded) assetsLoaded();
    };
    playerImage.src = "images/player.png";
    
    // 定義人物相關屬性
    let player = {
        x: Math.round((CanvasWidth/2)/zoom),
        y: Math.round((CanvasHeight/2)/zoom),
        currentDirection: moveConfig.STAND,
        direction: {
            [moveConfig.STAND] : {
                x: 0,
                y: 0
            },
            [`${moveConfig.DOWN}-1`] : {
                x: 17,
                y: 0
            },
            [`${moveConfig.DOWN}-2`] : {
                x: 34,
                y: 0
            },
            [`${moveConfig.UP}-1`] : {
                x: 125,
                y: 0
            },
            [`${moveConfig.UP}-2`] : {
                x: 142,
                y: 0
            },
            [`${moveConfig.LEFT}-1`] : {
                x: 69,
                y: 0
            },
            [`${moveConfig.LEFT}-2`] : {
                x: 87,
                y: 0
            },
            [`${moveConfig.RIGHT}-1`] : {
                x: 160,
                y: 0
            },
            [`${moveConfig.RIGHT}-2`] : {
                x: 178,
                y: 0
            }
        }
    };

    // 人物動作事件
    player.move = (direction) => {
        player.currentDirection = 
            moveMappingImage[direction][player.currentDirection] 
            ?? moveMappingImage[direction][moveConfig.DEFAULT]
        if (direction === moveConfig.LEFT 
         || direction === moveConfig.RIGHT) {
            player.x += (direction === moveConfig.LEFT 
                ? -speed: speed);
        }
        if (direction === moveConfig.UP
         || direction === moveConfig.DOWN) {
            player.y += (direction === moveConfig.UP 
                ? -speed: speed);
        }
        assetsLoaded();
    };

    // 初始事件
    function assetsLoaded() {
        ...
        ctx.drawImage(playerImage, 
            player.direction[player.currentDirection].x, 
            player.direction[player.currentDirection].y, 
            zoom-2, 
            zoom, 
            player.x * zoom, 
            player.y * zoom, 
            zoom, 
            zoom
        );
    }
}
  1. 新增人物走路的鍵盤監聽事件(上下左右)
window.onload = () => {
    ...
    // 鍵盤監聽事件-人物走路
    document.onkeydown = (e) => {
        const actionList = {
            "ArrowLeft": () => player.move(moveConfig.LEFT),
            "ArrowUp": () => player.move(moveConfig.UP),
            "ArrowRight": () => player.move(moveConfig.RIGHT),
            "ArrowDown": () => player.move(moveConfig.DOWN)
        }
        const action = actionList[e.key]
        if (action) {
            action();
        }
    };
}

按照以上四個步驟就能完成 RPG 小遊戲的基礎設定,包含地圖樣式、人物走動的方向、位置以及人物的角度樣式等等!恭喜你~如果跟著完成到這邊已經走進了遊戲的世界,明天讓我們繼續完成剩下的遊戲互動,例如走著走著吃到經驗值或是遇到別的人物。


上一篇
第 21 幅 - 用 Canvas 做告白專用刮刮樂,「這款」中獎機率最高!
下一篇
第 23 幅 - 用 Canvas 復刻 Pokemon ,做個 RPG 小遊戲吧!(下)
系列文
拾起 Canvas,人人都是大藝術家!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言