文件结构
├── base // 定义游戏开发基础类
│ ├── animatoin.js // 帧动画的简易实现
│ ├── pool.js // 对象池的简易实现
│ └── sprite.js // 游戏基本元素精灵类
├── libs
│ ├── symbol.js // ES6 Symbol简易兼容
│ └── weapp-adapter.js // 小游戏适配器
├── npc
│ └── enemy.js // 敌机类
├── player
│ ├── bullet.js // 子弹类
│ └── index.js // 玩家类
├── runtime
│ ├── background.js // 背景类
│ ├── gameinfo.js // 用于展示分数和结算界面
│ └── music.js // 全局音效管理器
├── databus.js // 管控游戏状态
└── main.js // 游戏入口主函数
├── base // 定义游戏开发基础类
│ ├── animatoin.js // 帧动画的简易实现
代码内容
import Sprite from './sprite'
import DataBus from '../databus'
let databus = new DataBus()
const __ = {
timer: Symbol('timer'),
}
/**
* 简易的帧动画类实现
*/
export default class Animation extends Sprite {
constructor(imgSrc, width, height) {
super(imgSrc, width, height)
// 当前动画是否播放中
this.isPlaying = false
// 动画是否需要循环播放
this.loop = false
// 每一帧的时间间隔
this.interval = 1000 / 60
// 帧定时器
this[__.timer] = null
// 当前播放的帧
this.index = -1
// 总帧数
this.count = 0
// 帧图片集合
this.imgList = []
/**
* 推入到全局动画池里面
* 便于全局绘图的时候遍历和绘制当前动画帧
*/
databus.animations.push(this)
}
/**
* 初始化帧动画的所有帧
* 为了简单,只支持一个帧动画
*/
initFrames(imgList) {
imgList.forEach((imgSrc) => {
let img = new Image()
img.src = imgSrc
this.imgList.push(img)
})
this.count = imgList.length
}
// 将播放中的帧绘制到canvas上
aniRender(ctx) {
ctx.drawImage(
this.imgList[this.index],
this.x,
this.y,
this.width * 1.2,
this.height * 1.2
)
}
// 播放预定的帧动画
playAnimation(index = 0, loop = false) {
// 动画播放的时候精灵图不再展示,播放帧动画的具体帧
this.visible = false
this.isPlaying = true
this.loop = loop
this.index = index
if ( this.interval > 0 && this.count ) {
this[__.timer] = setInterval(
this.frameLoop.bind(this),
this.interval
)
}
}
// 停止帧动画播放
stop() {
this.isPlaying = false
if ( this[__.timer] )
clearInterval(this[__.timer])
}
// 帧遍历
frameLoop() {
this.index++
if ( this.index > this.count - 1 ) {
if ( this.loop ) {
this.index = 0
}
else {
this.index--
this.stop()
}
}
}
}
│ ├── pool.js // 对象池的简易实现
const __ = {
poolDic: Symbol('poolDic')
}
/**
* 简易的对象池实现
* 用于对象的存贮和重复使用
* 可以有效减少对象创建开销和避免频繁的垃圾回收
* 提高游戏性能
*/
export default class Pool {
constructor() {
this[__.poolDic] = {
}
}
/**
* 根据对象标识符
* 获取对应的对象池
*/
getPoolBySign(name) {
return this[__.poolDic][name] || ( this[__.poolDic][name] = [] )
}
/**
* 根据传入的对象标识符,查询对象池
* 对象池为空创建新的类,否则从对象池中取
*/
getItemByClass(name, className) {
let pool = this.getPoolBySign(name)
let result = ( pool.length
? pool.shift()
: new className() )
return result
}
/**
* 将对象回收到对象池
* 方便后续继续使用
*/
recover(name, instance) {
this.getPoolBySign(name).push(instance)
}
}
│ └── sprite.js // 游戏基本元素精灵类
/**
* 游戏基础的精灵类
*/
export default class Sprite {
constructor(imgSrc = '', width= 0, height = 0, x = 0, y = 0) {
this.img = new Image()
this.img.src = imgSrc
this.width = width
this.height = height
this.x = x
this.y = y
this.visible = true
}
/**
* 将精灵图绘制在canvas上
*/
drawToCanvas(ctx) {
if ( !this.visible )
return
ctx.drawImage(
this.img,
this.x,
this.y,
this.width,
this.height
)
}
/**
* 简单的碰撞检测定义:
* 另一个精灵的中心点处于本精灵所在的矩形内即可
* @param{Sprite} sp: Sptite的实例
*/
isCollideWith(sp) {
let spX = sp.x + sp.width / 2
let spY = sp.y + sp.height / 2
if ( !this.visible || !sp.visible )
return false
return !!( spX >= this.x
&& spX <= this.x + this.width
&& spY >= this.y
&& spY <= this.y + this.height )
}
}
├── libs
│ ├── symbol.js
/**
* 对于ES6中Symbol的极简兼容
* 方便模拟私有变量
*/
let Symbol = window.Symbol
let idCounter = 0
if (!Symbol) {
Symbol = function Symbol(key) {
return `__${
key}_${
Math.floor(Math.random() * 1e9)}_${
++idCounter}__`
}
Symbol.iterator = Symbol('Symbol.iterator')
}
window.Symbol = Symbol
// ES6 Symbol简易兼容
│ └── weapp-adapter.js // 小游戏适配器
/******/ (function(modules) {
// webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {
}
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {
},
/******/ id: moduleId,
/******/ loaded: false
/******/ }
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__)
/******/ // Flag the module as loaded
/******/ module.loaded = true
/******/ // Return the exports of the module
/******/ return module.exports
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = ""
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0)
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
'use strict'
var _window2 = __webpack_require__(1)
var _window = _interopRequireWildcard(_window2)
function _interopRequireWildcard(obj) {
if (obj && obj.__esModule) {
return obj } else {
var newObj = {
}; if (obj != null) {
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key] } } newObj.default = obj; return newObj } }
var global = GameGlobal
function inject() {
_window.addEventListener = _window.canvas.addEventListener = function (type, listener) {
_window.document.addEventListener(type, listener)
}
_window.removeEventListener = _window.canvas.removeEventListener = function (type, listener) {
_window.document.removeEventListener(type, listener)
}
var _wx$getSystemInfoSync = wx.getSystemInfoSync(),
platform = _wx$getSystemInfoSync.platform
// 开发者工具无法重定义 window
if (typeof __devtoolssubcontext === 'undefined' && platform === 'devtools') {
for (var key in _window) {
var descriptor = Object.getOwnPropertyDescriptor(global, key)
if (!descriptor || descriptor.configurable === true) {
Object.defineProperty(window, key, {
value: _window[key]
})
}
}
for (var _key in _window.document) {
var _descriptor = Object.getOwnPropertyDescriptor(global.document, _key)
if (!_descriptor || _descriptor.configurable === true) {
Object.defineProperty(global.document, _key, {
value: _window.document[_key]
})
}
}
window.parent = window
} else {
for (var _key2 in _window) {
global[_key2] = _window[_key2]
}
global.window = _window
window = global
window.top = window.parent = window
}
}
if (!GameGlobal.__isAdapterInjected) {
GameGlobal.__isAdapterInjected = true
inject()
}
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
'use strict'
Object.defineProperty(exports, "__esModule", {
value: true
})
exports.cancelAnimationFrame = exports.requestAnimationFrame = exports.clearInterval = exports.clearTimeout = exports.setInterval = exports.setTimeout = exports.canvas = exports.location = exports.localStorage = exports.HTMLElement = exports.FileReader = exports.Audio = exports.Image = exports.WebSocket = exports.XMLHttpRequest = exports.navigator = exports.document = undefined
var _WindowProperties = __webpack_require__(2)
Object.keys(_WindowProperties).forEach(function (key) {
if (key === "default" || key === "__esModule") return
Object.defineProperty(exports, key, {
enumerable: true,
get: function get() {
return _WindowProperties[key]
}
})
})
var _constructor = __webpack_require__(3)
Object.keys(_constructor).forEach(function (key) {
if (key === "default" || key === "__esModule") return
Object.defineProperty(exports, key, {
enumerable: true,
get: function get() {
return _constructor[key]
}
})
})
var _Canvas = __webpack_require__(9)
var _Canvas2 = _interopRequireDefault(_Canvas)
var _document2 = __webpack_require__(10)
var _document3 = _interopRequireDefault(_document2)
var _navigator2 = __webpack_require__(17)
var _navigator3 = _interopRequireDefault(_navigator2)
var _XMLHttpRequest2 = __webpack_require__(18)
var _XMLHttpRequest3 = _interopRequireDefault(_XMLHttpRequest2)
var _WebSocket2 = __webpack_require__(19)
var _WebSocket3 = _interopRequireDefault(_WebSocket2)
var _Image2 = __webpack_require__(11)
var _Image3 = _interopRequireDefault(_Image2)
var _Audio2 = __webpack_require__(12)
var _Audio3 = _interopRequireDefault(_Audio2)
var _FileReader2 = __webpack_require__(20)
var _FileReader3 = _interopRequireDefault(_FileReader2)
var _HTMLElement2 = __webpack_require__(4)
var _HTMLElement3 = _interopRequireDefault(_HTMLElement2)
var _localStorage2 = __webpack_require__(21)
var _localStorage3 = _interopRequireDefault(_localStorage2)
var _location2 = __webpack_require__(22)
var _location3 = _interopRequireDefault(_location2)
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj } }
exports.document = _document3.default
exports.navigator = _navigator3.default
exports.XMLHttpRequest = _XMLHttpRequest3.default
exports.WebSocket = _WebSocket3.default
exports.Image = _Image3.default
exports.Audio = _Audio3.default
exports.FileReader = _FileReader3.default
exports.HTMLElement = _HTMLElement3.default
exports.localStorage = _localStorage3.default
exports.location = _location3.default
// 暴露全局的 canvas
var canvas = new _Canvas2.default()
exports.canvas = canvas
exports.setTimeout = setTimeout
exports.setInterval = setInterval
exports.clearTimeout = clearTimeout
exports.clearInterval = clearInterval
exports.requestAnimationFrame = requestAnimationFrame
exports.cancelAnimationFrame = cancelAnimationFrame
/***/ }),
/* 2 */
/***/ (function(module, exports) {
"use strict"
Object.defineProperty(exports, "__esModule", {
value: true
})
var _wx$getSystemInfoSync = wx.getSystemInfoSync(),
screenWidth = _wx$getSystemInfoSync.screenWidth,
screenHeight = _wx$getSystemInfoSync.screenHeight,
devicePixelRatio = _wx$getSystemInfoSync.devicePixelRatio
var innerWidth = exports.innerWidth = screenWidth
var innerHeight = exports.innerHeight = screenHeight
exports.devicePixelRatio = devicePixelRatio
var screen = exports.screen = {
availWidth: innerWidth,
availHeight: innerHeight
}
var performance = exports.performance = {
now: function now() {
return Date.now() / 1000
}
}
var ontouchstart = exports.ontouchstart = null
var ontouchmove = exports.ontouchmove = null
var ontouchend = exports.ontouchend = null
/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {
'use strict'
Object.defineProperty(exports, "__esModule", {
value: true
})
exports.HTMLCanvasElement = exports.HTMLImageElement = undefined
var _HTMLElement3 = __webpack_require__(4)
var _HTMLElement4 = _interopRequireDefault(_HTMLElement3)
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj } }
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function") } }
function _possibleConstructorReturn(self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called") } return call && (typeof call === "object" || typeof call === "function") ? call : self }
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass) } subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass }
var HTMLImageElement = exports.HTMLImageElement = function (_HTMLElement) {
_inherits(HTMLImageElement, _HTMLElement)
function HTMLImageElement() {
_classCallCheck(this, HTMLImageElement)
return _possibleConstructorReturn(this, (HTMLImageElement.__proto__ || Object.getPrototypeOf(HTMLImageElement)).call(this, 'img'))
}
return HTMLImageElement
}(_HTMLElement4.default)
var HTMLCanvasElement = exports.HTMLCanvasElement = function (_HTMLElement2) {
_inherits(HTMLCanvasElement, _HTMLElement2)
function HTMLCanvasElement() {
_classCallCheck(this, HTMLCanvasElement)
return _possibleConstructorReturn(this, (HTMLCanvasElement.__proto__ || Object.getPrototypeOf(HTMLCanvasElement)).call(this, 'canvas'))
}
return HTMLCanvasElement
}(_HTMLElement4.default)
/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {
'use strict'
Object.defineProperty(exports, "__esModule", {
value: true
})
var _createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor) } } return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor } }()
var _Element2 = __webpack_require__(5)
var _Element3 = _interopRequireDefault(_Element2