有積分的自己自行下載吧,沒有積分的兄弟可以評論留郵箱,看到後會給你們發過去。
遊戲截圖
設計過程
1. 設計目標:推箱子游戲
2. 設計思路:
1.1 觀察遊戲玩法 鏈接:http://www.4399.com/flash/202073.htm
2.2 分析遊戲:遊戲的玩法是將箱子通過小人推送到目標點,所有箱子到達目標點即爲勝利。
小人移動的必要條件:1. 玩家按下方向鍵。 2. 移動終點有效(空白或箱子可被推動)。
小人的移動分析:
1)小人的移動終點爲空白。此時小人直接到達即可。
2)小人的移動終點爲牆壁。此時小人的移動無效。
3)小人的移動終點當前被箱子所佔。此時需要對箱子的移動進行判定。
箱子移動的必要條件:1.箱子只可在小人的推動下才可進行移。2.移動終點有效(無障礙物)。
箱子的移動分析:
1)箱子的移動終點爲空白。可被推動。
2)箱子的移動終點爲牆壁或被其他箱子所佔。此時箱子不可推動。
3)箱子的移動終點爲目標點。箱子的移動後需要改變箱子的顯示以達到提示用戶效果。
3. 設計實現:
遊戲使用 css3 中的 canvas 畫布設計實現(。。。介紹自行百度吧)
沒有基礎可以去 part1 文件夾裏面看基礎。用到的核心就行畫布畫圖片。
圖片資源來自於 4399 遊戲,爲方便設計,圖片已經 ps 處理,圖片大小爲 80*80 像素。畫布使用大小50*50,需要進行縮放。
js 需要設計的東西:
核心代碼:
1. 判斷箱子或小人是否可移動。
2. 繪製遊戲界面。
用戶事件: 按下移動鍵後的處理函數。
4. 具體實現參見 part2 裏面代碼註釋寫的非常清楚
part1
index.html
<!DOCTYPE html> <!-- 聲明當前文檔爲 html5文檔 -->
<html lang="zh-CN"> <!-- 語言爲中文 -->
<head>
<meta charset="UTF-8"> <!-- 字符編碼:utf-8 -->
<title>推箱子</title> <!-- 頁面的標題 -->
<style>
/* Flex 佈局語法教程 https://www.runoob.com/w3cnote/flex-grammar.html */
/* 去除瀏覽器默認的內外邊距 */
* {
margin: 0;
padding: 0;
}
html, body {
width: 100%;
height: 100%;
}
/* 使用 flex 佈局, 主軸爲水平方向, 起點在左端, 在主軸上的居中對齊 */
.game-frame-wrap {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
justify-content: center;
background-color: #eee; /* 方便區分 */
}
/* 使用 flex 佈局, 主軸爲垂直方向, 起點在上沿. 起點在上端, 在主軸上的居中對齊 */
.game-frame {
display: flex;
flex-direction: column;
justify-content: center;
background-color: white; /* 方便區分 */
}
#game-canvas {
width: 300px;
height: 300px;
background-color: green;
}
/* 以上的樣式代碼, 使遊戲窗口保持在瀏覽器的水平垂直中間位置 */
</style>
</head>
<body>
<div class="game-frame-wrap">
<img id="wall" src="images/wall-sheet0.png" width="80" height="80" alt="">
<div class="game-frame">
<canvas id="game-canvas" width="300" height="300"></canvas>
</div>
</div>
<script>
window.onload = function () {
// HTML5 Canvas 使用教程 https://www.runoob.com/html/html5-canvas.html
// 獲取 canvas dom 對象
var canvas = document.getElementById("game-canvas");
console.log(canvas, typeof canvas, "canvas");
// 獲取在 canvas 畫布上繪圖的對象
var ctx = canvas.getContext("2d");
console.log(ctx, typeof ctx, "ctx");
// 1. 使用頁面顯示的圖片繪圖
var wallImg = document.getElementById("wall");
// 在畫布 x:10, y:10 的位置開始繪圖. 左上角座標原點
ctx.drawImage(wallImg, 10, 10);
// 2. 使用圖片資源直接繪圖, 這種方式需要將圖片資源加載好之後才能開始畫圖
var img = new Image();
img.src = "./images/crate-sheet0.png";
img.onload = function () {
ctx.drawImage(img, 100, 100);
}
};
</script>
</body>
</html>
crate-sheet0.png wall-sheet0.png
part2
index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>推箱子</title>
<link rel="stylesheet" href="./css/index.css">
<script src="js/gameMap.js"></script>
<script type="text/javascript" src="js/index.js"></script>
</head>
<body>
<div class="game-frame-wrap">
<div class="game-frame">
<canvas id="game-canvas" width="600" height="600"></canvas>
<div id="game-info"></div>
</div>
</div>
</body>
</html>
gameMap.js
var gameMap=[];
// 0: 背景圖片
// 7: 地板圖片
// 1: 牆壁圖片
// 2: 目標點圖片
// 3: 箱子圖片
// 4: 小人圖片
// 5: 箱子就位
gameMap[0]=[
[0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,1,1,1,0,0,0,0,0],
[0,0,0,0,1,2,1,0,0,0,0,0],
[0,0,0,0,1,7,1,1,1,1,0,0],
[0,0,1,1,1,3,7,3,2,1,0,0],
[0,0,1,2,7,3,4,1,1,1,0,0],
[0,0,1,1,1,1,3,1,0,0,0,0],
[0,0,0,0,0,1,2,1,0,0,0,0],
[0,0,0,0,0,1,1,1,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0]];
gameMap[1]=[
[0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,1,1,1,1,1,0,0,0,0,0],
[0,0,1,4,7,7,1,0,0,0,0,0],
[0,0,1,7,3,3,1,0,1,1,1,0],
[0,0,1,7,3,7,1,0,1,2,1,0],
[0,0,1,1,1,7,1,1,1,2,1,0],
[0,0,0,1,1,7,7,7,7,2,1,0],
[0,0,0,1,7,7,7,1,7,7,1,0],
[0,0,0,1,7,7,7,1,1,1,1,0],
[0,0,0,1,1,1,1,1,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0]];
gameMap[2]=[
[0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,1,1,1,1,1,1,1,0,0,0],
[0,0,1,7,7,7,7,7,1,1,1,0],
[0,1,1,3,1,1,1,7,7,7,1,0],
[0,1,7,4,7,3,7,7,3,7,1,0],
[0,1,7,2,2,1,7,3,7,1,1,0],
[0,1,1,2,2,1,7,7,7,1,0,0],
[0,0,1,1,1,1,1,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0]];
gameMap[3]=[
[0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,1,1,1,1,0,0,0,0,0],
[0,0,1,1,7,7,1,0,0,0,0,0],
[0,0,1,4,3,7,1,0,0,0,0,0],
[0,0,1,1,3,7,1,1,0,0,0,0],
[0,0,1,1,7,3,7,1,0,0,0,0],
[0,0,1,2,3,7,7,1,0,0,0,0],
[0,0,1,2,2,5,2,1,0,0,0,0],
[0,0,1,1,1,1,1,1,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0]];
gameMap[4]=[
[0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,1,1,1,1,1,0,0,0,0],
[0,0,0,1,4,7,1,1,1,0,0,0],
[0,0,0,1,7,3,7,7,1,0,0,0],
[0,0,1,1,1,7,1,7,1,1,0,0],
[0,0,1,2,1,7,1,7,7,1,0,0],
[0,0,1,2,3,7,7,1,7,1,0,0],
[0,0,1,2,7,7,7,3,7,1,0,0],
[0,0,1,1,1,1,1,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0]];
index.js 部分代碼
window.onload = function () {
var BLOCK_WIDTH = 50, BLOCK_HEIGHT = 50, WIDTH_SIZE = 12, HEIGHT_SIZE = 12;
var BLOCK_ENUM = {
background: 0,
floor: 7,
wall: 1,
point: 2,
crate: 3,
player: 4,
crate_ok: 5
}; // 定義一些常量
var currentGameMap; // 當前的遊戲地圖數據 12*12,每格 50px
var CURRENT_GAME_MAP; // 當前的遊戲地圖數據的原始數據
var currentLevel; // 當前等遊戲地圖的等級
var moveCount; // 移動次數
var imageMapping; // 存儲圖片數據
var personLocation = {x: 0, y: 0, position: 'down'}; // 存儲小人的位置,方向
var gameInfo = document.getElementById('game-info');
var canvas = document.getElementById('game-canvas');
var ctx = canvas.getContext('2d');
initGame(0);
// 玩家按鍵事件監測,單次按下移動一次。不需要按着連續移動。
window.onkeydown = onkeydown;
window.onkeyup = function () {
window.onkeydown = onkeydown;
};
function onkeydown(event) {
switch (event.key) {
case 'ArrowUp': // 上鍵頭 w
case 'w':
tryMove('up');
break;
case 'ArrowDown': // 下箭頭 s
case 's':
tryMove('down');
break;
case 'ArrowLeft': // 左鍵頭 a
case 'a':
tryMove('left');
break;
case 'ArrowRight':
case 'd':
tryMove('right'); // 右箭頭 d
break;
}
window.onkeydown = null; // 與 window.onkeyup 一塊限制按下按鍵觸發一次事件
}
// 處理用戶按鍵事件(小人移動),可以移動則移動
function tryMove(position) {
personLocation.position = position;
var p1, p2;
...
// 如果小人能夠移動的話,更新遊戲數據,並重繪地圖
if(dealMove(p1, p2)){
moveCount++;
showGameInfo();
}
initGameMap(); // 繪當前更新了數據的地圖, 不移動也重繪一次改變小人的朝向
// 延時一下等待畫布畫好
setTimeout(function () {
// 如果移動完成了進入下一關
if (checkFinish()) {
alert('恭喜過關!!');
initGame(currentLevel + 1)
}
},100)
}
// 處理小人移動
function dealMove(p1, p2) {
...
}
//判斷是否推成功
function checkFinish() {
...
}
function initGame(level) {
if (level >= gameMap.length) {
alert('恭喜已經全部過關!!');
return
}
currentLevel = level; // 初始化地圖等級
moveCount = 0; // 初始化移動次數
imageMapping = {
background: './images/background.png',
floor: './images/floor.png',
wall: './images/wall.png',
point: './images/point.png',
crate: './images/crate.png',
player: './images/player.png',
crate_ok: './images/crate_ok.png'
}; // 創建圖片數據源
currentGameMap = gameMap[currentLevel]; // 初始化遊戲地圖數據
CURRENT_GAME_MAP = copyArray(currentGameMap);
// 預加載圖片資源數據,加載好出初始化遊戲地圖
loadImage(imageMapping, function () {
initGameMap();
showGameInfo();
});
}
// 圖片資源必須加載完畢才能使用,所以這裏使用回調函數來初始化地圖
function loadImage(srcs, callback) {
var loadedCount = 0, // 已經預加載好的圖片數
imgCount = 0; // 需要預加載的圖片數
for (var count in imageMapping) {
imgCount++;
}
for (var key in imageMapping) {
if(imageMapping.hasOwnProperty(key)){ // 對 對象的 key 進行一下檢查
var src = imageMapping[key];
imageMapping[key] = new Image();
imageMapping[key].src = src;
imageMapping[key].onload = function () {
//判斷是否所有的圖片都預加載完成
if (++loadedCount >= imgCount && callback instanceof Function) {
callback();
}
}
}
}
}
// 繪畫地圖的函數
function initGameMap() {
for (var i = 0; i < HEIGHT_SIZE; i++) {
for (var j = 0; j < WIDTH_SIZE; j++) {
drawBlock(j, i)
}
}
}
function drawBlock(x, y) {
// 因爲小人和目標點的背景是透明且小人有方向,所以小人和目標點要單獨處理
var image = undefined;
var blockType = currentGameMap[y][x];
switch (blockType) {
case BLOCK_ENUM.background:
image = imageMapping.background;
break;
case BLOCK_ENUM.floor: // 地板圖片
image = imageMapping.floor;
break;
case BLOCK_ENUM.wall: // 牆壁圖片
image = imageMapping.wall;
break;
//case BLOCK_ENUM.point: // 目標點圖片, 需要背景圖單獨畫
// image = imageMapping.point;
// break;
case BLOCK_ENUM.crate: // 箱子圖片
image = imageMapping.crate;
break;
//case BLOCK_ENUM.player: // 小人圖片, 因爲小人有面朝方向,所以要單獨畫
// image = imageMapping.player;
// personLocation.x = x;
// personLocation.y = y;
// break;
case BLOCK_ENUM.crate_ok: // 箱子已經到位
image = imageMapping.crate_ok;
break;
}
if (image) {
// 將80*80的圖片壓縮成50*50畫
ctx.drawImage(image, 0, 0, 80, 80, BLOCK_WIDTH * x, BLOCK_HEIGHT * y, BLOCK_WIDTH, BLOCK_HEIGHT);
} else {
ctx.drawImage(imageMapping.floor, 0, 0, 80, 80, BLOCK_WIDTH * x, BLOCK_HEIGHT * y, BLOCK_WIDTH, BLOCK_HEIGHT);
if (blockType === BLOCK_ENUM.point) {
ctx.drawImage(imageMapping.point, 0, 0, 80, 80, BLOCK_WIDTH * x, BLOCK_HEIGHT * y, BLOCK_WIDTH, BLOCK_HEIGHT);
} else if (blockType === BLOCK_ENUM.player) {
drawPerson(x, y)
}
}
}
function drawPerson(x, y) {
personLocation.x = x;
personLocation.y = y;
var sx = 0, sy = 0; // 小人圖像在圖片中的位置
switch (personLocation.position) {
case 'up':
sy = 0;
break;
case 'down':
sy = 80;
break;
case 'left':
sy = 160;
break;
case 'right':
sy = 240;
break;
}
ctx.drawImage(imageMapping.player, sx, sy, 80, 80, BLOCK_WIDTH * x, BLOCK_HEIGHT * y, BLOCK_WIDTH, BLOCK_HEIGHT);
}
function showGameInfo(){
gameInfo.innerText = '第' + (currentLevel + 1) + '關,總共' + gameMap.length + '關。移動次數: ' + moveCount;
}
//克隆二維數組
function copyArray(arr) {
var b = [];
for (var i = 0; i < arr.length; i++) {
var c= [];
for (var j = 0; j < arr.length; j++) {
c.push(arr[i][j]);
}
b.push(c)
}
return b;
}
};