# 物件導向JS繼承 - 遊戲實作
### **撰寫程式:HTML | CSS(SASS) | JS(jQuery ∕ 原生JS)**
###### codepen:https://codepen.io/liu_0821/pen/XWYZgRV

## HTML
> ###### 原生HTML撰寫
```
<div class="game"> <!--遊戲主體-->
<div class="board b1"></div>
<div class="board b2"></div>
<div class="ball"></div>
<div class="info">
<h2 class="infoText">Start Game</h2>
<button class="start" onclick = "newGame.startGame()">開始</button>
</div>
<div class="grade"></div>
</div>
```
****
## CSS(SASS)
> ###### 與原生CSS不同之處,原生CSS使用大括弧{}套用CSS特性,SASS則利用縮排區分HTML之間的層級關係。
```
*
font-family: "微軟正黑體"
font-weight: 900
html,body
background-color: #222
color: white
display: flex
justify-content: center
align-items: center
height: 100%
margin: 0
.game
width: 500px
height: 500px
position: relative
border: 5px solid white
overflow: hidden
.board
background-color: #ffab2d
.ball
background-color: white
.info
width: 100%
height: 100%
position: absolute
left: 0
top: 0
background-color: #222
color: white
display: flex
justify-content: center
align-items: center
//垂直排列
flex-direction: column
.start
width: 80px
height: 50px
border-radius: 25px
border: 1px solid white
font-size: 18px
background-color: #222
color: white
cursor: pointer
.grade
padding: 10px
```
****
## JS(jQuery ∕ 原生JS)
> ###### 使用原生JS與jQuery撰寫,利用JS物件導向的繼承來作為整體程式開發的主軸。
###### 遊戲物件與繼承範圍
```
let gameObject = function(position,size,selector){
this.$el = $(selector);
this.position = position;
this.size = size ;
this.$el.css("position","absolute");
this.updateCss(); // 把初始化的條件更新到螢幕上
};
// 更新遊戲物件
gameObject.prototype.updateCss = function(){
this.$el.css('left',this.position.x);
this.$el.css('top',this.position.y);
this.$el.css('width',this.size.width);
this.$el.css('height',this.size.height);
};
// 計算遊戲碰撞
gameObject.prototype.collide = function(otherObject){
// this => 與其他物件
var inRangeX = otherObject.position.x > this.position.x && otherObject.position.x < this.position.x + this.size.width;
var inRangeY = otherObject.position.y > this.position.y && otherObject.position.y < this.position.y + this.size.height;
// 回傳 是否有同時滿足在他的範圍內
return inRangeX && inRangeY
};
```
###### 球體物件與更新
```
// 球球物件
let Ball = function(){
this.size = {width:15,height:15}
this.position = {x:250 ,y:250}
this.velocity = {x:-3,y:5} // 球體速度
// 繼承三步驟
//1.呼叫初始化函數
gameObject.call(this,this.position,this.size,".ball")
}
//2.讓ball可以使用gameObject的函數
Ball.prototype = Object.create(gameObject.prototype);
//3.將建構函數返回到自己原本建立的函數
Ball.prototype.constructor = Ball.constructor;
// 球球速度更新
Ball.prototype.update = function(){
this.position.x += this.velocity.x;
this.position.y += this.velocity.y;
this.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
}
};
// 球球初始化
Ball.prototype.init = function(){
this.position = {x:250 ,y:250}
// 球體速度 改成隨機速度 Math.random()原本是0~1的之間亂數
var speed = 8;
var angle = Math.random()*Math.PI*2
this.velocity = {
x: speed*Math.cos(angle),
y: speed*Math.sin(angle)
}
this.update();
};
// 建立球球
let newBall = new Ball()
// setInterval(function(){
// newBall.update() //呼叫球球移動
// },30)
```
###### 設立板子物件
```
let Board = function (position,selector){
this.size = {width:100,height:15}
// 1.呼叫初始化函數
gameObject.call(this,position,this.size,selector)
};
// 2.讓Board可以使用gameObject的函數
Board.prototype = Object.create(gameObject.prototype);
// 3.將建構函數返回到自己原本建立的函數
Board.prototype.constructor = Board.constructor;
// 檢查board1的板子不超出邊界
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();
};
// 建立板子
let board1 = new Board(
{ x : 0 , y : 30},".b1"
);
let board2 = new Board(
{ x : 0 , y : 455},".b2"
);
```
###### 遊戲開始物件與綁定鍵盤事件
```
let Game = function(){
this.timer = null;
this.grade = 0 ;
// 執行鍵盤事件
this.initControl();
this.control = {}; // 新增物件管理鍵盤按的變數
}
// 使用者控制 綁定鍵盤事件
Game.prototype.initControl = function(){
let _this = this;
$(window).keydown(function(evt){
_this.control[evt.key] = true ;
console.log(_this.control)
});
$(window).keyup(function(evt){
_this.control[evt.key] = false ;
console.log(_this.control)
});
}
//設定遊戲開始之前的倒數
Game.prototype.startGame = function(){
var _this = this ;
var time = 3;
this.grade = 0; // 初始分數為0
newBall.init(); //球球初始化
$("button").hide();
var timer = setInterval(function(){
$(".infoText").text(time);
time--;
if(time<0){
clearInterval(timer)
$(".info").hide();
_this.startGameMain()
}
},1000);
};
// 建立遊戲函數
Game.prototype.startGameMain = function(){
let _this = this; //代表game的this
// 偵測球體碰撞、輸贏狀態
this.timer = setInterval(function(){
// 判斷板子有無碰撞到球
if(board1.collide(newBall)){
console.log("hit 1")
// 打到後回彈
newBall.velocity.y = -newBall.velocity.y;
//打到板子後球體速度會變化
newBall.velocity.x *= 1.1;
newBall.velocity.y *= 1.1;
newBall.velocity.x += 0.5-Math.random();
newBall.velocity.y += 0.5-Math.random();
}
if(board2.collide(newBall)){
console.log("hit 2")
newBall.velocity.y = -newBall.velocity.y;
_this.grade += 10;
}
// 判斷有無撞到邊界
if(newBall.position.y < 0){
console.log("Board 1 lose")
_this.endGame("computer lose")
}
if(newBall.position.y > 500){
console.log("Board 2 lose")
_this.endGame("you lose")
}
// 判斷鍵盤按鍵
if(_this.control["ArrowLeft"]){
board2.position.x -= 12;
}
if(_this.control["ArrowRight"]){
board2.position.x += 12;
}
// 上方板子移動(以中心點來判斷,如果球在右邊向右跑,反之)
// 如果滿足條件就+8,反之+0
board1.position.x += newBall.position.x > board1.position.x + board1.size.width/2 ? 12:0 ;
board1.position.x += newBall.position.x < board1.position.x + board1.size.width/2 ? -12:0 ;
// 呼叫板子更新css
board1.update() ;
board2.update() ;
newBall.update();
//遊戲內更新自己的成績
$(".grade").text(_this.grade)
},30)
};
// 遊戲結束計算
Game.prototype.endGame = function(res){
clearInterval(this.timer)
$(".info").show();
$("button").show();
$(".infoText").html(res + "<br>分數:" + this.grade);
};
let newGame = new Game();
//newGame.startGame(); // 呼叫遊戲開始
```
##### ヽ(∀゚ )人(゚∀゚)人( ゚∀)人(∀゚ )人(゚∀゚)人( ゚∀)ノヽ(∀゚ )人(゚∀゚)人( ゚∀)人(∀゚ )人(゚∀゚)人( ゚∀)人(∀゚ )人( ゚∀)人
##### 以上 如果註解哪裡有錯誤或有問題,歡迎提出來一起討論~~~~
---