用jQuery手寫一個小遊戲

今天給你們帶來一個小遊戲css

體驗連接CodePenhtml

碼雲源碼連接Giteevue

要求:熟悉JavaScript繼承的概念git

遊戲預覽



玩法:開局一個球 兩塊板子。其中最上方是電腦板子,會跟着球跑。球達到板子上回彈回來。打到你的板子上也是回彈出去。若是達到了上下邊界。遊戲結束bash

控制你的板子就用方向左右鍵dom

接到一個球+10分ide


基礎佈局部分(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繼承的方式來寫這個遊戲。首先先定義一個GameObjectflex

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點鐘了。睡覺~~~~


謝謝各位

相關文章
相關標籤/搜索