用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点钟了。睡觉~~~~


谢谢各位

相关文章
相关标签/搜索