用jQuery手寫一個小遊戲
今天給大家帶來一個小遊戲
要求:熟悉JavaScript繼承的概念
遊戲預覽
玩法:開局一個球 兩塊板子。其中最上方是電腦板子,會跟著球跑。球達到板子上回彈回來。打到你的板子上也是回彈出去。如果達到了上下邊界。遊戲結束
控制你的板子就用方向左右鍵
接到一個球+10分
基礎佈局部分(HTML+CSS)
遊戲部分,我們按照以下圖示尺寸設定
html:
<div class="game"> //遊戲本體 <div class="board b1"> //電腦的板子(上板子) </div> <div class="board b2"> //你的板子(下板子) </div> <div class="ball"></div> //球體 <div class="info"> <h2 class="infoText">開始遊戲</h2> <button class="start">點選這裡</button> </div> <div class="grade">0</div> //左上角分數卡 </div>複製程式碼
CSS :
.game { width: 500px; height: 500px; position: relative; border: 5px solid #fff; background-color: #222; } .board { background-color: #FF644E; } .ball { background-color: #fff; } .info { width: 100%; height: 100%; position: absolute; left: 0; top: 0; color: white; background-color: #222; display: flex; justify-content: center; align-items: center; flex-direction: column; } .grade { padding: 10px; }複製程式碼
邏輯部分(JavaScript)
我們採用JavaScript繼承的方式來寫這個遊戲。首先先定義一個GameObject
let GameObject = function (position,size,selector){ this.$el = $(selector) //選擇器 this.position = position //遊戲物體位置 this.size = size //遊戲物體大小 this.$el.css("position","absolute") //設定其Css為絕對定位 this.updateCss() } GameObject.prototype.updateCss = function(){ this.$el.css("left",this.position.x+"px") this.$el.css("top",this.position.y+"px") this.$el.css("width",this.size.width+"px") this.$el.css("height",this.size.height+"px") }複製程式碼
首先
$el為選擇器。代表jQuery的元素選擇器
position為元素定位的位置
size為元素大小
設定在原型鏈上的 updateCss方法為元素位置,大小更新方法。按照當前物件的屬性數值更新
那我們先建立一個球(Ball)物件。繼承GameObject
let Ball = function () { this.size = {width: 15, height: 15}; //球的大小 this.position = {x: 250, y: 250}; //球的位置 this.velocity = {x: 5, y: 5}; //球的速度 GameObject.call(this,this.position,{width: 15, height: 15},".ball") //繼承GameObject。並將引數和自身傳入 }; Ball.prototype = Object.create(GameObject.prototype); //將Ball的原型鏈連線GameObjecr的原型鏈 Ball.prototype.constructor = Ball.constructor //因為連線,所以需要重新指向建構函式。將原型鏈的建構函式指向自己的建構函式複製程式碼
因為球只有一個,所以引數我們寫死在物件裡面
我們例項化一個球物件
let ball = new Ball();複製程式碼
接著螢幕中央就會有一個球
接下來開始繪製兩個可移動的板子
let Board = function (position, sel) { this.size = { //鎖定板子大小 width: 100, height: 15 }; GameObject.call(this, position, this.size, sel); //對接父物件 }; Board.prototype = Object.create(GameObject.prototype); //對接父物件原型鏈 Board.prototype.constructor = Board.constructor; //更改原型鏈上的構造為自己的構造複製程式碼
然後new 兩塊板子
let board1 = new Board({x: 0, y: 30}, '.b1'); let board2 = new Board({x: 0, y: 455}, '.b2');複製程式碼
然後,我們讓球動起來
我們在Ball的原型鏈上定義一個update方法。來移動小球
Ball.prototype.update = function () { this.position.x += this.velocity.x; //x軸按速度移動 this.position.y += this.velocity.y; //Y軸按速度移動 this.updateCss(); //呼叫父物件的updateCss方法更新介面 if (this.position.x < 0 || this.position.x > 500) { //如果撞到了左右牆壁 this.velocity.x = -this.velocity.x; // 回彈 } if (this.position.y < 0 || this.position.y > 500) { //如果撞到了上下牆壁 this.velocity.y = -this.velocity.y; // 回彈 } };複製程式碼
如果球的 橫向邊界 小於0或者大於500,說明球碰到了左右牆壁
如果球的 縱向邊界 小於0或者大於500,說明球碰到了上下牆壁
然後我們每隔30ms 呼叫一下小球的update函式。使其位置更新
setInterval(function () { ball.update(); }, 30)複製程式碼
如圖 :
這樣 小球就有了碰到障礙物反彈的能力了
接著我們刪掉這個定時器的程式碼
然後,我們定義一個Game物件。這個物件不會繼承任何父物件。因為他只負責控制其他物體物件
let Game = function () { this.timer = null; //唯一timer 負責開始遊戲結束遊戲的timer this.grade = 0; //分數 this.initControl(); //鍵盤監聽事件 this.control = {}; //這個放置各個鍵盤按鍵情況的物件 }; 複製程式碼
因為我們有鍵盤要控制板子的移動,所以我們要加監聽事件
Game.prototype.initControl = function () { let _this = this; //防止this作用域混淆 $(window).keydown(function (evt) { //按鍵按下 _this.control[evt.key] = true; //設定當前的key value為true }); $(window).keyup(function (evt) { //按鍵抬起 _this.control[evt.key] = false; //設定當前的key value為false }) };複製程式碼
根據我們的遊戲規則,小球碰到上下牆壁要判別輸贏。碰到上下板子要回彈
所以我們在GameObject的原型鏈上定義一個碰撞方法collide
GameObject.prototype.collide = function (otherObject) { let inRangeX = otherObject.position.x > this.position.x && otherObject.position.x < this.position.x + this.size.width; let inRangeY = otherObject.position.y > this.position.y && otherObject.position.y < this.position.y + this.size.height; return inRangeX && inRangeY; };複製程式碼
其引數是另一個物體物件。
inRangeX 的判別式:當另一個物體的X值大於你的X值 且 另個物體X值小於你的X值+你寬度的時候,返回true。否則false
inRangeY 的判別式:當另一個物體的Y值大於你的 Y 值 且 另個物體 Y 值小於你的 Y 值+你高度的時候,返回true。否則false
然後返回兩個判別式的情況
如果都為true,說明兩個物體相撞了
這樣我們在Game物件定義一個startGameMain方法。代表是我們遊戲控制器主體
Game.prototype.startGameMain = function () { let _this = this; //作用域!!! this.timer = setInterval(function () { //唯一定時器 if (board1.collide(ball)) { //如果一號板子撞到了球 console.log("碰到了1號板子"); ball.velocity.y = -ball.velocity.y; //Y反向運動 } if (board2.collide(ball)) { //如果二號板子撞到了球 console.log("碰到了2號板子"); _this.grade += 10; //自己的分數+10 ball.velocity.y = -ball.velocity.y; } ball.update(); //球體更新方法 $(".grade").text(this.grade); //jQuery更新分數 }, 30) //每隔30ms走一次 };複製程式碼
然後
let game = new Game(); game.startGameMain();複製程式碼
看一看效果
接著在 startGameMain 函式內,繼續編寫
如果球碰到上板子。說明上板子輸了 如果碰到下板子,下板子輸了
if (ball.position.y < 0) { console.log("第一個板子輸了"); _this.endGame("你贏了"); //後面的結束遊戲方法 } if (ball.position.y > 500) { console.log("第二個板子輸了"); _this.endGame("你輸了"); }複製程式碼
接著我們讓上板子跟著球跑
我們現在板子Board物件內定義一個update方法。更新板子的座標和UI
Board.prototype.update = function () { this.updateCss(); };複製程式碼
然後繼續在startGameMain 函式內 寫板子跟球跑的邏輯
board1.position.x += ball.position.x > board1.position.x + board1.size.width / 2 ? 12 : 0; board1.position.x += ball.position.x < board1.position.x + board1.size.width / 2 ? -12 : 0; board1.update();複製程式碼
如果球的X座標 > 板子X座標+板子寬度/2,那麼板子X +12。向右跑
如果球的X座標 < 板子X座標+板子寬度/2,那麼板子X -12 。 向左跑
如圖
但是細心的朋友可能會發現,板子超出邊界了。
所以我們就限制板子最小x為0,最大x為 容器width-板子width
於是我們重寫一下Board的update方法
Board.prototype.update = function () { if (this.position.x < 0) { this.position.x = 0; } if (this.position.x + this.size.width > 500) { this.position.x = 500 - this.size.width; } this.updateCss(); };複製程式碼
這樣再看看~~
接著寫我方板子 鍵盤控制事件
還是在startGameMain函式內
if (_this.control["ArrowLeft"]) { //如果左鍵 board2.position.x -= 8; //二號板子左移8 } if (_this.control["ArrowRight"]) { //如果右鍵 board2.position.x += 8; //二號板子右移8 } 複製程式碼
board2.update(); 複製程式碼
這樣我們的鍵盤也可以操控了。球也能正常回彈
我們繼續寫endGame函式
Game.prototype.endGame = function (res) { clearInterval(this.timer); //清除定時器 $(".infoText").html(res + '<br>分數:' + this.grade); //展示分數 $(".info").show(); //展示資訊 };複製程式碼
然後我們在加一個startGame的函式
Game.prototype.startGame = function () { let time = 3; //倒計時3秒 let _this = this; this.grade = 0; //初始化分數0 ball.init(); //稍後用到 let timer = setInterval(function () { $(".infoText").text(time); time--; if (time < 0) { //如果時間<0 clearInterval(timer); //清除定時器 $(".info").hide(); //隱藏資訊 _this.startGameMain(); //開始主要的遊戲函式 } }, 1000) };複製程式碼
我們在HTML裡面新增info資訊的元素
<div class="game"> <div class="board b1"> </div> <div class="board b2"> </div> <div class="ball"></div> <div class="info"> //新增的地方 <h2 class="infoText">開始遊戲</h2> <button class="start">點選這裡</button> </div> <div class="grade">0</div> </div>複製程式碼
最下面我們呼叫一下startGame
let game = new Game(); $(".start").click(function () { game.startGame(); })複製程式碼
這樣一個比較完整的遊戲完成了
但是這看起來有點傻。因為他每次只向一個方向去發車
我們可以使用JavaScript中的三角函式,解決這個問題
首先我們找到Ball物件。把裡面的速度引數,抽出為一個函式。取名叫init
Ball.prototype.init = function () { this.position = {x: 250, y: 250}; let randomDeg = Math.random() * 2 * Math.PI; this.velocity = { x: Math.cos(randomDeg) * 8, y: Math.sin(randomDeg) * 8 } };複製程式碼
然後 Ball物件內只剩下
let Ball = function () { this.size = {width: 15, height: 15}; this.init(); GameObject.call(this, this.position, this.size, '.ball'); };複製程式碼
我們來仔細講一下這個init函式
首先我們先鎖定速度為10,這個是首要條件。所以我們先產生一個隨機角度:
let randomDeg = Math.random() * 2 * Math.PI;複製程式碼
1PI 為180度 2PI為360度
然後我們再隨機一個小數。可以得到一個360度以內的任意角
接著,我們可以根據三角函式,cos和sin
sin是斜邊 / 對邊,cos是斜邊 / 鄰邊。我們如果知道了角度和長度,就可以知道XY的速度分別是多少
X長度 = 斜邊長 * Cos(角度)
Y長度 = 斜邊長 * Sin(角度)如圖
這也就是數學中 向量 的概念
然後我們再看看我們的遊戲:
顯然比之前合理多了。
寫完這篇文章已經 凌晨3點鐘了。睡覺~~~~