Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
tiancheng2000 committed Aug 14, 2018
2 parents f3f2594 + cfbb47d commit 86e8be9
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 28 deletions.
76 changes: 74 additions & 2 deletions js/common/config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,76 @@
export default class Config {

let instance
class Config {
constructor() {
if (instance)
return instance
instance = this
//----------------------------
this.UpdateRate = 60
this.CtrlLayers = { //玩家操控层
Background: {
DefaultActive: false
}
}
this.GodMode = false
//----------------------------
}
}

const subscription = new Map() //propName --> callbackSet
const propNameStack = []
const observable = obj => {
return (obj.__isProxy !== undefined) ? obj : //already a Proxy
new Proxy(obj, {
get(target, key, receiver) {
//return Config[key]
if (key === '__isProxy') //Proxy property
return true
if (key === 'subscribe') //Proxy public function
return this.subscribe
const result = Reflect.get(target, key, receiver)
if (typeof result === 'object' && !result.__isProxy) {
const observableResult = observable(result)
Reflect.set(target, key, observableResult, receiver)
observableResult.keyStroke = (target.keyStroke === undefined) ? key : target.keyStroke + '.' + key
return observableResult
}
return result
},
set(target, key, value, receiver) {
if (target[key] != value){
let res = Reflect.set(target, key, value, receiver)
if (!res)
return false
if (!value.__isProxy){
let propName = (target.keyStroke === undefined) ? key : target.keyStroke + '.' + key
this.onPropertyChanged(propName, value)
}
return res
}
return true
},
subscribe(propName, callback) {
let callbackSet = subscription.get(propName)
|| subscription.set(propName, new Set()).get(propName) //cannot use WeakSet
callbackSet.add(callback)
},
onPropertyChanged(name, value) {
let callbackSet = subscription.get(name)
if (callbackSet !== undefined)
for (let callback of callbackSet) {
callback(name, value) //IMPROVE: allow interruption
}
}
})
}

Config.UpdateRate = 60 //每秒总数据更新次数
const configProxy = observable(new Config())

//NOTE: cannot use ES6 style export
// export {
// Config as configProxy
// }
module.exports = {
Config: configProxy
}
9 changes: 9 additions & 0 deletions js/common/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,14 @@ export default class Util {
&& point.y >= area.startY
&& point.y <= area.endY)
}

static findNext(arr, value){
for (let i = 0; i < arr.length; i++) {
if (arr[i] === value) {
return arr[(i + 1) % arr.length]
}
}
return undefined
}

}
6 changes: 5 additions & 1 deletion js/databus.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default class DataBus {
this.bullets = []
this.enemys = []
this.animations = []
this.gameOver = false
this.gameStatus = DataBus.GameRunning
}

/**
Expand All @@ -50,3 +50,7 @@ export default class DataBus {
this.pool.recover('bullet', bullet)
}
}

DataBus.GameRunning = 0
DataBus.GameOver = 1
DataBus.GamePaused = 2
107 changes: 90 additions & 17 deletions js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import BackGround from './runtime/background'
import GameInfo from './runtime/gameinfo'
import Music from './runtime/music'
import DataBus from './databus'
import Config from './common/config'
//import Config from './common/config'
import ControlLayer from './base/controllayer'
import Util from './common/util'

let ctx = canvas.getContext('2d')
let databus = new DataBus()

//const UpdateRate = require('./common/config.js').UpdateRate
const Config = require('./common/config.js').Config

/**
* 游戏主函数
Expand All @@ -27,6 +28,9 @@ export default class Main {
['touchstart', 'touchmove', 'touchend'].forEach((type) => {
canvas.addEventListener(type, this.touchEventHandler.bind(this))
})
;['UpdateRate', 'CtrlLayers.Background.DefaultActive', 'GodMode'].forEach(propName => {
Config.subscribe(propName, this.onConfigChanged.bind(this))
})

//3.初次/重新启动
this.restart()
Expand Down Expand Up @@ -61,6 +65,9 @@ export default class Main {
restart() {
databus.reset()

//0.与通用类的关联
console.log(`Restart: Config.UpdateRate=${Config.UpdateRate}`)

//1.需重置的游戏数据、玩家操控处理机制
this.updateInterval = 1000 / Config.UpdateRate
this.bg = new BackGround(ctx)
Expand All @@ -69,7 +76,8 @@ export default class Main {
this.music = new Music()
this.ctrlLayerUI = new ControlLayer('UI', [this.gameinfo])
this.ctrlLayerSprites = new ControlLayer('Sprites', [this.player])
this.ctrlLayerBackground = new ControlLayer('Background', [this.bg], false)
this.ctrlLayerBackground = new ControlLayer('Background', [this.bg],
Config.CtrlLayers.Background.DefaultActive) //this.CtrlLayers.Background.DefaultActive)

//2.两个主循环重启
if (this.updateTimer)
Expand All @@ -86,6 +94,21 @@ export default class Main {
)
}

pause() {
if (databus.gameStatus == DataBus.GameOver)
return
databus.gameStatus = DataBus.GamePaused
this.ctrlLayerSprites.active = false
this.ctrlLayerBackground.active = false
}
resume() {
if (databus.gameStatus == DataBus.GameOver)
return
databus.gameStatus = DataBus.GameRunning
this.ctrlLayerSprites.active = true
this.ctrlLayerBackground.active = Config.CtrlLayers.Background.DefaultActive
}

/**
* 随着帧数变化的敌机生成逻辑
* 帧数取模定义成生成的频率
Expand Down Expand Up @@ -118,13 +141,15 @@ export default class Main {
}
})

for (let i = 0, il = databus.enemys.length; i < il; i++) {
let enemy = databus.enemys[i]
if (!Config.GodMode){
for (let i = 0, il = databus.enemys.length; i < il; i++) {
let enemy = databus.enemys[i]

if (this.player.isCollideWith(enemy)) {
databus.gameOver = true
if (this.player.isCollideWith(enemy)) {
databus.gameStatus = DataBus.GameOver

break
break
}
}
}
}
Expand All @@ -139,35 +164,57 @@ export default class Main {
// 2.当上层发生过处理时下层不再处理(parent-catch)
// 3.同一层中,有一个元素处理过(队头优先)其他元素即不再处理(sibling-catch)
let upperLayerHandled = false
;[this.ctrlLayerUI, this.ctrlLayerSprites, this.ctrlLayerBackground]
.forEach((ctrlLayer) => {
for (let ctrlLayer of [this.ctrlLayerUI, this.ctrlLayerSprites, this.ctrlLayerBackground]) {
if (upperLayerHandled)
return false //stop handling
break //stop handling
if (!ctrlLayer.active)
return true //next layer
continue //next layer
//console.log(`${e.type}: ${ctrlLayer.name}`)
ctrlLayer.elements.some((element) => {
//console.log(`${e.type}: ${element.__proto__.constructor.name}`)
element.onTouchEvent(e.type, x, y, ((res) => {
switch (res.message) {
//--- Game Status Switch ---
case 'restart':
this.restart()
break
case 'pause':
this.pause()
break
case 'resume':
this.resume()
break
//--- Setting Commands ---
case 'switchUpdateRate':
Config.UpdateRate = Util.findNext(res.optionList, Config.UpdateRate)
break
case 'switchBulletSpeed':
wx.showToast({ title: 'not implemented' })
break
case 'switchBulletType':
wx.showToast({ title: 'not implemented' })
break
case 'youAreGod':
Config.GodMode = Util.findNext(res.optionList, Config.GodMode)
break
case 'backgroundActive':
Config.CtrlLayers.Background.DefaultActive = Util.findNext(res.optionList, Config.CtrlLayers.Background.DefaultActive)
break
}
if (res.message.length > 0){
upperLayerHandled = true
return true //if any element handled the event, stop iteration
}
}).bind(this))
})
})
}

}

//-- 游戏数据【更新】主函数 ----
update(timeElapsed) {
if (databus.gameOver)
return;
if ([DataBus.GameOver, DataBus.GamePaused].indexOf(databus.gameStatus) > -1)
return

this.bg.update()

Expand All @@ -188,12 +235,38 @@ export default class Main {
this.music.playShoot()
}

if (databus.gameOver) {
//GameOver can only be caused by collisionDetection
if (databus.gameStatus == DataBus.GameOver) {
this.ctrlLayerSprites.active = false
this.ctrlLayerBackground.active = false
}
}

onConfigChanged(key, value){
console.log(`onConfigChanged: ${key}=${value}`)
switch (key){
case 'UpdateRate':
this.updateInterval = 1000 / Config.UpdateRate
if (this.updateTimer)
clearInterval(this.updateTimer)
this.updateTimer = setInterval(
this.bindloopUpdate,
this.updateInterval
)
break
case 'CtrlLayers.Background.DefaultActive':
wx.showToast({
title: `Active=${Config.CtrlLayers.Background.DefaultActive}`,
})
break
case 'GodMode':
wx.showToast({
title: value ? '无敌!' : '小心,无敌取消',
})
break
}
}

//-- 游戏数据【渲染】主函数 ----
render() {
ctx.clearRect(0, 0, canvas.width, canvas.height)
Expand All @@ -217,7 +290,7 @@ export default class Main {
this.gameinfo.renderGameScore(ctx, databus.score)

// 游戏结束停止帧循环
if (databus.gameOver) {
if (databus.gameStatus == DataBus.GameOver) {
this.gameinfo.renderGameOver(ctx, databus.score)
}
}
Expand Down
58 changes: 50 additions & 8 deletions js/runtime/gameinfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,71 @@ const screenHeight = window.innerHeight
let atlas = new Image()
atlas.src = 'images/Common.png'

const SettingCommands = {
textList: ['每秒数据更新频率切换', '子弹速度切换', '子弹类型切换', '无敌模式切换', '背景层事件响应切换'],
commandList: ['switchUpdateRate', 'switchBulletSpeed', 'switchBulletType', 'youAreGod', 'backgroundActive'],
optionListList: [[60, 6], [10, 20], ['single', 'double'], [false, true], [false, true]]
}

export default class GameInfo {
constructor() {
this.showGameOver = false
}

onTouchEvent(type, x, y, callback) {
if (this.showGameOver && type == 'touchstart') {
if (Util.inArea({x, y}, this.btnRestart)) {
callback({ message: 'restart' })
this.showGameOver = false
}
switch (type) {
case 'touchstart':
if (Util.inArea({ x, y }, this.areaSetting)){
callback({ message: 'pause' })
let commandIndex
wx.showActionSheet({
itemList: SettingCommands.textList,
success: function (res) {
commandIndex = res.tapIndex
},
complete: function () {
if (commandIndex !== undefined){
callback({
message: SettingCommands.commandList[commandIndex],
optionList: SettingCommands.optionListList[commandIndex]
})
}
callback({ message: 'resume' })
}
})
}
else if (this.showGameOver && Util.inArea({ x, y }, this.btnRestart)) {
callback({ message: 'restart' })
this.showGameOver = false
}
break
}
}

renderGameScore(ctx, score) {
ctx.fillStyle = "#ffffff"
ctx.font = "20px Arial"

//visualize area boundary
// ctx.drawImage(
// atlas,
// 202, 6, 39, 24,
// 10, 10,
// 28, 25
// )

//candidate icons: ⏲⏱⏰⏳🏹🏆🏅🙌👾👁🐲👹😎☏✧☟😘🎈🎊⚙❤🐷💥👁‍🗨💬🔄💠㊙💦🍙🍒💎
ctx.fillText(
score,
10,
30
'🏅 ' + score, //设定图标
10, 10 + 20
)

this.areaSetting = {
startX: 10,
startY: 10,
endX: 10 + 28, //ctx.font = '20px Arial'
endY: 10 + 25
}
}

renderGameOver(ctx, score) {
Expand Down

0 comments on commit 86e8be9

Please sign in to comment.