先放下我玩游戏的效果图:



关于游戏最后的结束部分其实我还没有截图,看着挺好看的,后面的效果

再放作者大大的项目地址:https://github.com/panruiplay/PixelFire

接下来我们一起学习项目哇哇哇

这个项目用到了webpack,其实这个webpack的功能我觉得这个项目中用到了就是因为可以使用localhost:8080直接打开这种

我们可以仔细研究代码看看是不是我这样认为的

index.html中,有所有会在界面上渲染的静态页面效果

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>元素射击</title>
  6. </head>
  7. <body>
  8. <div class="root">
  9. <!--加载面板-->
  10. <div class="panel loading" id="loading">
  11. <div class="box">
  12. <div class="text">正在加载</div>
  13. </div>
  14. </div>
  15. <!--加载面板-->
  16. <div class="panel intoGame hide" id="intoGame">
  17. <div class="btn" id="intoBtn">点击进入游戏</div>
  18. </div>
  19. <!--菜单面板-->
  20. <div class="panel menu hide" id="menu">
  21. <h1 class="animated fadeIn">元素射击</h1>
  22. <h2 class="animated fadeIn" style="animation-delay: .2s">Ver 1.0</h2>
  23. <ul>
  24. <li id="btn-start" class="btn animated flipInX" style="animation-delay: 1s">开始游戏</li>
  25. <li id="btn-help" class="btn animated flipInX" style="animation-delay: 1.1s">操作说明</li>
  26. <li id="btn-shop" class="btn animated flipInX" style="animation-delay: 1.2s">游戏商店</li>
  27. </ul>
  28. </div>
  29. <!--帮助面板-->
  30. <div class="panel help hide">
  31. <div class="dir-box">
  32. <div class="center">W</div>
  33. <section>
  34. <div>A</div>
  35. <div>S</div>
  36. <div>D</div>
  37. </section>
  38. </div>
  39. <p>WASD控制移动,鼠标控制射击方向。</p>
  40. <div class="btn" id="back-menu2">返回</div>
  41. </div>
  42. </div>
  43. </body>
  44. </html>

index.js中定义了游戏运行的环境,以及引入相应的css文件和主运行文件game.js

  1. //index.js
  2. import './style/index.css'
  3. import './style/block.css'
  4. import './style/animation.css'
  5. import './style/pointer.css'
  6. import Game from './script/class/Game'
  7. Game.init()
  8. if(window.env === 'dev'){
  9. window.game = Game
  10. }

在game.js中,引入很多功能性js文件,实现Ui界面,背景音乐播放,还有按键控制以及我方和敌方输和赢的原则

  1. //game.js
  2. import UI from './UI'
  3. import { $, addEventLock, addEventOnce } from '../utils'
  4. import Music from './Music'
  5. import Chain from 'func-chain'
  6. import Rect from './Rect'
  7. import Green from './Block/Unit/Green'
  8. import Control from './Control'
  9. import { pointerExpansion } from './Block/decorators'
  10. import { pointDeg } from '../math'
  11. import UnitFactory from './Block/Unit/Factory'
  12. import Data from '../Data/index'
  13. import QuadTree from './QuadTree'
  14. import Block from './Block/Block'
  15. import Combination from './Block/Combination'
  16. class Game {
  17. width = 1000
  18. height = 600
  19. centerX = 500
  20. centerY = 300
  21. domRoot = $('.root') // DOM根节点对象
  22. user = null // 用户角色
  23. userGroup = [] // 用户组block
  24. enemyGroup = [] // 敌方组block
  25. bounds = new Rect(0, 0, this.width, this.height)
  26. enemyGroupQuadTree = new QuadTree(this.bounds)
  27. // 初始化
  28. init() {
  29. this.UI = new UI()
  30. this.Music = new Music()
  31. this.Control = new Control()
  32. this.Music.loadMusic(() => {
  33. this.UI.change('intoGame')
  34. addEventOnce('#intoBtn', 'click', () => {
  35. this.UI.change('menu')
  36. setTimeout(() => {
  37. this.Music.playBgm('bgm_main')
  38. }, 700)
  39. })
  40. // this.Music.playBgm('bgm_main')
  41. // this.UI.change('menu')
  42. })
  43. // 用户单位
  44. this.user = new Green(this.centerX, this.centerY).speedClear()
  45. pointerExpansion(this.user, 's1')
  46. // 注册基本按钮事件
  47. this.eventBase()
  48. // 注册用户角色方向键控制
  49. this.userControl()
  50. }
  51. // 基本按钮事件
  52. eventBase() {
  53. // 开始游戏
  54. addEventLock('#btn-start', 'click', (e, unLock) => {
  55. setTimeout(unLock, 1000)
  56. this.startGame()
  57. })
  58. }
  59. // 用户键控
  60. userControl() {
  61. this.Control.disableDirectionKey()
  62. this.Control.onDirChange((count, dir) => {
  63. if(count) {
  64. this.user.setAngle(dir)
  65. } else {
  66. this.user.speedClear()
  67. }
  68. })
  69. this.Control.disableMouse = true
  70. }
  71. //--------------------------//
  72. // 开始游戏(关卡)
  73. startGame(k = 1) {
  74. let { user, Control: control, UI: ui, Music: music, userGroup, enemyGroup } = this,
  75. { width, height } = this,
  76. data = Data['k' + k],
  77. time = 0
  78. Chain()
  79. > ui.hide
  80. > function (next) { // 音乐切换
  81. music.playBgm('bgm_bat')
  82. next()
  83. }
  84. > user.birth.args(true)
  85. > function () { // 打开用户控制
  86. control.enableDirectionKey()
  87. control.disableMouse = false
  88. userGroup.push(user)
  89. requestAnimationFrame(loop)
  90. }
  91. || Chain.go()
  92. let createEnemy = (time) => {
  93. let tmp = []
  94. for(let i = data.length - 1; i >= 0; i--) {
  95. let v = data[i]
  96. if(v.createTime && time >= v.createTime) {
  97. let { x, y } = v
  98. if(x === 'user') {
  99. x = this.userX()
  100. y = this.userY()
  101. }
  102. let enemy = UnitFactory(v.enemy, x || this.randomX(), y || this.randomY())
  103. if(enemy instanceof Combination) {
  104. // 组合敌人
  105. enemy.done((enemyArr) => {
  106. for(let j = 0; j < enemyArr.length; j++) {
  107. let enemyArrElement = enemyArr[j]
  108. enemyArrElement.birth(true, () => enemyGroup.push(enemyArrElement))
  109. }
  110. })
  111. } else {
  112. // 单个敌人
  113. enemy.birth(true, () => enemyGroup.push(enemy))
  114. }
  115. continue
  116. }
  117. tmp.push(v)
  118. }
  119. data = tmp
  120. }
  121. let loop = () => {
  122. let { centerX, centerY } = user.rect
  123. let { userGroup, enemyGroup } = this
  124. let enemyGroupQuadTree = new QuadTree(this.bounds)
  125. // 生产敌人
  126. createEnemy(time)
  127. // 敌人行动
  128. for(let i = enemyGroup.length - 1; i >= 0; i--) {
  129. let enemy = enemyGroup[i],
  130. { rect } = enemy
  131. // 如果已经死亡
  132. if(enemy.isDestroy) {
  133. enemyGroup.splice(i, 1)
  134. continue
  135. }
  136. // 杀死所有超出边界的单位
  137. if(rect.x > width || rect.y > height || rect.x + rect.width < 0 || rect.y + rect.height < 0) {
  138. enemy.destroy(false)
  139. enemyGroup.splice(i, 1)
  140. continue
  141. }
  142. enemy.next().update()
  143. enemyGroupQuadTree.insert(rect)
  144. }
  145. // 用户行动 碰撞:用户组所有单位与敌人组进行碰撞检测
  146. for(let i = userGroup.length - 1; i >= 0; i--) {
  147. let friend = userGroup[i],
  148. { rect } = friend
  149. // 如果已经死亡
  150. if(friend.isDestroy) {
  151. userGroup.splice(i, 1)
  152. continue
  153. }
  154. // 杀死所有超出边界的单位
  155. if(rect.x > width || rect.y > height || rect.x + rect.width < 0 || rect.y + rect.height < 0) {
  156. friend.destroy(false)
  157. userGroup.splice(i, 1)
  158. continue
  159. }
  160. friend.next().update()
  161. let arr = enemyGroupQuadTree.retrieve(friend.rect)
  162. for(let j = arr.length - 1; j >= 0; j--) {
  163. let enemyRect = arr[j]
  164. // 如果发生碰撞
  165. if(Block.isCollision(enemyRect, friend.rect)) {
  166. Block.collision(enemyRect.block, friend)
  167. }
  168. }
  169. }
  170. // 用户指针更新
  171. user.pointer.angle = pointDeg(centerX, centerY, control.mouseX, control.mouseY)
  172. time++
  173. requestAnimationFrame(loop)
  174. }
  175. }
  176. //--------------------------//
  177. randomX() {
  178. return Math.random() * this.width >> 0
  179. }
  180. randomY() {
  181. return Math.random() * this.height >> 0
  182. }
  183. userX() { return this.user.rect.centerX }
  184. userY() { return this.user.rect.centerY }
  185. }
  186. export default new Game()

接下来我们来分析引入的js文件

utils.js中封装了一些公共方法

  1. /* -------------∽-★-∽---元素 & 事件---∽-★-∽------------- */
  2. // 搜索器
  3. export function $(selector) {
  4. return document.querySelector(selector)
  5. }
  6. // 搜索器(全部)
  7. export function $$(selector) {
  8. return document.querySelectorAll(selector)
  9. }
  10. // 创建dom元素
  11. export function createDom(name, cls = '') {
  12. let dom = document.createElement(name)
  13. dom.className = cls
  14. return dom
  15. }
  16. // 事件代理 dom可以是元素或者字符串
  17. export function addEventAgent(dom, targetCls, type, fn) {
  18. let reg = RegExp('(^| )' + targetCls + '($| )')
  19. let _fn = function (e) {
  20. if(reg.test(e.target.className)) fn(e)
  21. }
  22. if(typeof dom === 'string') dom = $(dom)
  23. dom.addEventListener(type, _fn)
  24. return function () {
  25. dom.removeEventListener(type, _fn)
  26. }
  27. }
  28. // 添加事件 事件触发一次后上锁,1秒后解锁
  29. export function addEventLock(dom, type, fn) {
  30. if(typeof dom === 'string') dom = $(dom)
  31. let lock = false
  32. let _fn = function (e) {
  33. if(lock) return
  34. lock = true
  35. setTimeout(() => lock = false, 1000)
  36. fn(e)
  37. }
  38. dom.addEventListener(type, _fn)
  39. return function () {
  40. dom.removeEventListener(type, _fn)
  41. }
  42. }
  43. // 添加一次性事件
  44. export function addEventOnce(dom, type, fn) {
  45. if(typeof dom === 'string') dom = $(dom)
  46. let _fn = function (e) {
  47. fn(e)
  48. dom.removeEventListener(type, _fn)
  49. }
  50. dom.addEventListener(type, _fn)
  51. }
  52. /* -------------∽-★-∽---样式类---∽-★-∽------------- */
  53. // 是否包含某个样式名
  54. export function hasClass(classNameStr, targetCls) {
  55. let reg = RegExp('(^| )' + targetCls + '($| )')
  56. return reg.test(classNameStr)
  57. }
  58. // 添加样式
  59. export function addClass(dom, ...cls) {
  60. dom.className += ' ' + cls.join(' ')
  61. }
  62. // 删除样式
  63. export function removeClass(dom, ...cls) {
  64. let className = dom.className
  65. for(let i = 0; i < cls.length; i++) {
  66. className = className.replace(RegExp('(^| )' + cls[i] + '($| )', 'g'), ' ')
  67. }
  68. dom.className = className
  69. }
  70. /* -------------∽-★-∽---其它---∽-★-∽------------- */
  71. // 取得文件名,不包含后缀
  72. export function getFileName(url) {
  73. return url.split('/').pop().split('.')[0]
  74. }

ui.js主要是控制是否显示对应的界面

  1. //ui.js
  2. import { $$, addClass, addEventAgent, hasClass, removeClass } from '../utils'
  3. import Game from './Game'
  4. import Chain from 'func-chain'
  5. class UI {
  6. panels = {} // 所有的面板
  7. current = null // 当前显示的面板
  8. transitionTime = 700 // 面板过度时间
  9. constructor() {
  10. let arr = Array.from($$('.panel'))
  11. arr.forEach(v => {
  12. v.style.transition = `opacity ${this.transitionTime}ms`
  13. this.panels[v.id] = v
  14. })
  15. // 当前显示面板
  16. this.current = arr.find(v => !hasClass(v.className, 'hide'))
  17. // 注册所有Btn点击音效(所有有btn样式的元素点击时,产生音效)
  18. addEventAgent(Game.domRoot, 'btn', 'click', () => Game.Music.play('click'))
  19. }
  20. // 隐藏当前面板
  21. hide = (cb) => {
  22. addClass(this.current, 'opacity0')
  23. setTimeout(() => {
  24. addClass(this.current, 'hide')
  25. removeClass(this.current, 'opacity0')
  26. cb && cb()
  27. }, this.transitionTime)
  28. }
  29. // 显示面板
  30. show = (name, cb) => {
  31. let target = this.panels[name],
  32. time = this.transitionTime
  33. this.current = target
  34. addClass(target, 'opacity0')
  35. removeClass(target, 'hide')
  36. Chain()
  37. > function (next) { setTimeout(next, 50) }
  38. > function (next) {
  39. addClass(target, 'opacity1')
  40. setTimeout(next, time)
  41. }
  42. > function () {
  43. removeClass(target, 'opacity1', 'opacity0')
  44. cb && cb()
  45. }
  46. || Chain.go()
  47. }
  48. // 切换面板
  49. change = (name, cb) => {
  50. Chain()
  51. > this.hide
  52. > this.show.args(name)
  53. > cb
  54. || Chain.go()
  55. }
  56. }
  57. export default UI

music控制音乐的播放

  1. import { createDom, getFileName } from '../utils'
  2. // 取得所有音乐文件
  3. let context = require.context('../../assets/ogg', false),
  4. allOgg = context.keys().map(key => context(key))
  5. class Music {
  6. listMap = {} // 所有的音乐对象
  7. currentBgm = null // 当前播放的bgm
  8. constructor() {
  9. allOgg.forEach(v => this.listMap[getFileName(v)] = '/' + v)
  10. }
  11. // 加载所有音乐
  12. loadMusic(cb) {
  13. let listMap = this.listMap,
  14. all = []
  15. for(let listMapKey in listMap) {
  16. all.push(new Promise((resolve, reject) => {
  17. let audio = createDom('audio')
  18. audio.oncanplay = () => {
  19. listMap[listMapKey] = audio
  20. resolve()
  21. }
  22. audio.onerror = reject
  23. audio.src = listMap[listMapKey]
  24. }))
  25. }
  26. Promise.all(all)
  27. .then(() => cb && cb(true))
  28. .catch(() => cb && cb(false))
  29. }
  30. // 播放音效
  31. play(name) {
  32. let music = this.listMap[name]
  33. music.currentTime = 0
  34. music.loop = false
  35. music.play()
  36. }
  37. // 播放背景音乐(自动循环)
  38. playBgm(name) {
  39. if(this.currentBgm) this.currentBgm.pause()
  40. let music = this.listMap[name]
  41. this.currentBgm = music
  42. music.currentTime = 0
  43. music.loop = true
  44. music.play()
  45. }
  46. }
  47. export default Music

创建的方形对象?

  1. //src\script\class\Rect.js
  2. /**
  3. * 矩形对象
  4. * @property {number} x - 起始位置x
  5. * @property {number} y - 起始位置y
  6. * @property {number} centerX - 中心x位置
  7. * @property {number} centerY - 中心y位置
  8. * @property {number} width - 宽度
  9. * @property {number} height - 高度
  10. */
  11. class Rect {
  12. // 按中心点创建单位
  13. static centerCreate(centerX, centerY, width, height) {
  14. return new Rect(centerX - width / 2, centerY - height / 2, width, height)
  15. }
  16. constructor(x, y, width, height) {
  17. this.x = x
  18. this.y = y
  19. this.width = width
  20. this.height = height
  21. this.centerX = x + width / 2
  22. this.centerY = y + height / 2
  23. }
  24. // 更新中心位置
  25. update() {
  26. this.centerX = this.x + this.width / 2
  27. this.centerY = this.y + this.height / 2
  28. }
  29. /**
  30. * 切割矩形
  31. * @param {number} cX - 纵向切线 x坐标
  32. * @param {number} cY - 横向切线 y坐标
  33. * @return {Rect[]}
  34. */
  35. carve(cX, cY) {
  36. let result = [],
  37. temp = [],
  38. dX = cX - this.x,
  39. dY = cY - this.y,
  40. carveX = dX > 0 && dX < this.width,
  41. carveY = dY > 0 && dY < this.height
  42. // 切割XY方向
  43. if(carveX && carveY) {
  44. temp = this.carve(cX, this.y)
  45. while(temp.length) {
  46. result = result.concat(temp.shift().carve(this.x, cY))
  47. }
  48. // 只切割X方向
  49. } else if(carveX) {
  50. result.push(
  51. new Rect(this.x, this.y, dX, this.height),
  52. new Rect(cX, this.y, this.width - dX, this.height)
  53. )
  54. // 只切割Y方向
  55. } else if(carveY) {
  56. result.push(
  57. new Rect(this.x, this.y, this.width, dY),
  58. new Rect(this.x, cY, this.width, this.height - dY)
  59. )
  60. }
  61. return result
  62. }
  63. }
  64. export default Rect
  1. //定义小方块样式
  2. //src\script\class\Block\Unit\Green.js
  3. import { ShrinkGreedM } from '../../Animation/Pre'
  4. import { BoomGreen } from '../../Animation/Boom'
  5. import { SpreadGreen } from '../../Animation/Spread'
  6. import Block from '../Block'
  7. import { LaunchBullet } from '../../Skill/LaunchBullet'
  8. import BaseBullet from './BaseBullet'
  9. import { BoundsLimit } from '../decorators'
  10. class Green extends Block {
  11. static FactoryName = 'Green'
  12. className = 'background-green' // 方块样式
  13. preAni = new ShrinkGreedM() // 预警动画
  14. birthAni = new SpreadGreen() // 出生动画
  15. deathAni = new BoomGreen() // 死亡动画
  16. hp = 1
  17. atk = 10
  18. decorators = [BoundsLimit]
  19. skill = [[LaunchBullet, BaseBullet, 'user']]
  20. speed = 2
  21. angle = 0
  22. constructor(x, y) {
  23. super(x, y, 10, 10)
  24. }
  25. }
  26. export default Green
  1. //移动的方法
  2. //src\script\class\Animation\Animation.js
  3. import Game from '../Game'
  4. import { createDom } from '../../utils'
  5. /**
  6. * 动画类: new Animation().show()
  7. */
  8. class Ani {
  9. static baseClass = 'ani'
  10. dom = null // dom对象
  11. music = '' // 音乐
  12. width = 0
  13. height = 0
  14. constructor(width, height, className, music) {
  15. this.dom = createDom('div', Ani.baseClass + ' ' + className)
  16. this.width = width
  17. this.height = height
  18. this.music = music
  19. }
  20. show = (x, y, cb) => {
  21. x = x - this.width / 2
  22. y = y - this.height / 2
  23. this.dom.style.cssText = `left: ${x}px; top: ${y}px;`
  24. let end = () => {
  25. Game.domRoot.removeChild(this.dom)
  26. this.dom.removeEventListener('webkitAnimationEnd', end)
  27. this.dom.removeEventListener('animationend', end)
  28. cb && cb()
  29. }
  30. this.dom.addEventListener('webkitAnimationEnd', end)
  31. this.dom.addEventListener('animationend', end)
  32. if(this.music) Game.Music.play(this.music)
  33. Game.domRoot.appendChild(this.dom)
  34. }
  35. }
  36. export default Ani

创建各种各样移动的类对象?

  1. //src\script\class\Animation\Boom.js
  2. import Ani from './Animation'
  3. class BoomRed extends Ani {
  4. constructor() { super(10, 10, 'boom boom-red', 'del1') }
  5. }
  6. class BoomGreen extends Ani {
  7. constructor() { super(10, 10, 'boom boom-green', 'del1') }
  8. }
  9. class BoomYellow extends Ani {
  10. constructor() { super(10, 10, 'boom boom-yellow', 'del1') }
  11. }
  12. class BoomOrange extends Ani {
  13. constructor() { super(10, 10, 'boom boom-orange', 'del1') }
  14. }
  15. class BoomBlue extends Ani {
  16. constructor() { super(10, 10, 'boom boom-blue', 'del1') }
  17. }
  18. class BoomSBlue extends Ani {
  19. constructor() { super(10, 10, 'boom boom-s-blue', 'del2') }
  20. }
  21. export {
  22. BoomRed,
  23. BoomGreen,
  24. BoomYellow,
  25. BoomOrange,
  26. BoomBlue,
  27. BoomSBlue,
  28. }

定义了各种spread类型

  1. //src\script\class\Animation\Spread.js
  2. import Ani from './Animation'
  3. class SpreadRed extends Ani {
  4. constructor() { super(10, 10, 'spread spread-red') }
  5. }
  6. class SpreadGreen extends Ani {
  7. constructor() { super(10, 10, 'spread spread-green') }
  8. }
  9. class SpreadBlue extends Ani {
  10. constructor() { super(10, 10, 'spread spread-blue') }
  11. }
  12. export {
  13. SpreadRed,
  14. SpreadGreen,
  15. SpreadBlue,
  16. }

方块类,可以碰撞的对象

  1. //src\script\class\Block\Block.js
  2. import { createDom } from '../../utils'
  3. import Game from '../Game'
  4. import Chain from 'func-chain'
  5. import Rect from '../Rect'
  6. let pix = Math.PI / 180
  7. /**
  8. * 方块类:屏幕上一个可参与碰撞的基本单位
  9. */
  10. class Block {
  11. static baseClass = 'block-base'
  12. // 判断两个block是否碰撞
  13. static isCollision(rect1, rect2) {
  14. if(rect1.block.isDestroy || rect2.block.isDestroy) return false
  15. let b1x = rect1.x,
  16. b1y = rect1.y,
  17. b2x = rect2.x,
  18. b2y = rect2.y
  19. return !(
  20. b1y > rect2.height + b2y ||
  21. rect1.width + b1x < b2x ||
  22. rect1.height + b1y < b2y ||
  23. b1x > rect2.width + b2x
  24. )
  25. }
  26. // 撞击
  27. static collision(block1, block2) {
  28. block1.hp -= block2.atk
  29. block2.hp -= block1.atk
  30. if(block1.hp <= 0) block1.destroy()
  31. if(block2.hp <= 0) block2.destroy()
  32. }
  33. dom = null // DOM对象
  34. rect = null // 矩形对象
  35. className = '' // 方块样式
  36. preAni = null // 预警动画
  37. birthAni = null // 出生动画
  38. birthMusic = null // 出生音乐
  39. deathAni = null // 死亡动画
  40. skill = [] // 技能
  41. decorators = [] // 装饰
  42. destroyEvt = [] // 死亡事件列表
  43. hp = 1 // 生命值
  44. atc = 10 // 攻击力
  45. isDestroy = false // 是否已经死亡
  46. angle = 0 // 角度
  47. radian = 0 // 弧度
  48. speed = 0 // 速度
  49. vx = 0 // x轴移动速度
  50. vy = 0 // y轴移动速度
  51. constructor(centerX, centerY, width, height) {
  52. this.rect = Rect.centerCreate(centerX, centerY, width, height)
  53. }
  54. // 初始化
  55. init() {
  56. let r = this.rect
  57. this.rect.block = this
  58. this.dom = createDom('div', Block.baseClass + ' ' + this.className)
  59. this.dom.style.cssText = `left: ${r.x}px; top: ${r.y}px; width: ${r.width}px; height: ${r.height}px`
  60. this.skill = this.skill.map(v => {
  61. if(v instanceof Array) {
  62. let [constructor, ...args] = v
  63. return new constructor(this, ...args)
  64. } else {
  65. return new v(this)
  66. }
  67. })
  68. this.decorators.forEach(v => {
  69. if(v instanceof Array) {
  70. let [fn, ...args] = v
  71. fn(this, ...args)
  72. } else {
  73. v(this)
  74. }
  75. })
  76. this.decomposeSpeed()
  77. return this
  78. }
  79. // 在屏幕上显示
  80. birth = (hasAni = true, cb) => {
  81. // 如果还没有创建dom则自动init
  82. if(!this.dom) this.init()
  83. let that = this
  84. Chain()
  85. > function (next) {
  86. if(hasAni && that.preAni) {
  87. that.preAni.show(that.rect.centerX, that.rect.centerY, next)
  88. } else {
  89. next()
  90. }
  91. }
  92. > function () {
  93. if(hasAni && that.birthAni){
  94. that.birthAni.show(that.rect.centerX, that.rect.centerY)
  95. }
  96. if(that.birthMusic) Game.Music.play(that.birthMusic)
  97. Game.domRoot.appendChild(that.dom)
  98. that.isDestroy = false
  99. cb && cb()
  100. }
  101. || Chain.go()
  102. return this
  103. }
  104. // 销毁
  105. destroy = (hasAni = true) => {
  106. this.isDestroy = true
  107. if(hasAni && this.deathAni) this.deathAni.show(this.rect.centerX, this.rect.centerY)
  108. Game.domRoot.removeChild(this.dom)
  109. this.destroyEvt.forEach(v => v())
  110. }
  111. // 行动(下一帧)
  112. next = () => {
  113. this.skill.forEach(v => v.next())
  114. this.rect.x += this.vx
  115. this.rect.y += this.vy
  116. this.rect.update()
  117. return this
  118. }
  119. // 更新显示效果
  120. update = () => {
  121. let rect = this.rect
  122. this.dom.style.left = `${rect.x}px`
  123. this.dom.style.top = `${rect.y}px`
  124. return this
  125. }
  126. // 死亡事件
  127. onDestroy = (fn) => {
  128. this.destroyEvt.push(fn)
  129. return this
  130. }
  131. // 速度设为0
  132. speedClear() {
  133. this.vx = 0
  134. this.vy = 0
  135. return this
  136. }
  137. // 设置角度
  138. setAngle(deg) {
  139. this.angle = deg
  140. this.radian = deg * pix
  141. this.decomposeSpeed()
  142. return this
  143. }
  144. // 设置弧度
  145. setRadian(radian) {
  146. this.radian = radian
  147. this.decomposeSpeed()
  148. return this
  149. }
  150. // 速度分解
  151. decomposeSpeed() {
  152. this.vx = Math.cos(this.radian) * this.speed
  153. this.vy = Math.sin(this.radian) * this.speed
  154. return this
  155. }
  156. }
  157. export default Block
  1. //src\script\class\Skill\LaunchBullet.js
  2. import Skill from './Skill'
  3. import Game from '../Game'
  4. // 用户专用
  5. class LaunchBullet extends Skill {
  6. step = 0
  7. cd = 14
  8. constructor(block, bulletClass) {
  9. super(block)
  10. this.bulletClass = bulletClass
  11. }
  12. action(block) {
  13. let { pointer } = block,
  14. { rect: userRect } = Game.user
  15. let bullet = new this.bulletClass(userRect.centerX, userRect.centerY)
  16. bullet.init().birth(false, () => {
  17. bullet.setAngle(pointer.angle || 0)
  18. Game.userGroup.push(bullet)
  19. })
  20. }
  21. }
  22. export {
  23. LaunchBullet
  24. }

定义的子弹

  1. //src\script\class\Block\Unit\BaseBullet.js
  2. import Block from '../Block'
  3. import { directionExpansion } from '../decorators'
  4. class BaseBullet extends Block {
  5. className = 'san1'
  6. birthMusic = 'fire1'
  7. decorators = [[directionExpansion, 45]]
  8. speed = 6
  9. hp = 10
  10. atk = 10
  11. constructor(x, y) {
  12. super(x, y, 10, 10)
  13. }
  14. }
  15. export default BaseBullet
  1. //src\script\class\Block\decorators.js
  2. import Pointer from './Pointer'
  3. import Game from '../Game'
  4. /* 类装饰 */
  5. // block的显示样式会根据自身angle属性进行旋转
  6. export function directionExpansion(block, angleFix = 0) {
  7. let _update = block.update
  8. block.update = () => {
  9. block.dom.style.transform = `rotate(${block.angle + angleFix}deg)`
  10. _update.call(block)
  11. }
  12. }
  13. // 边界限制,方法不可以超出边界
  14. export function BoundsLimit(block) {
  15. let _next = block.next
  16. block.next = () => {
  17. _next.call(block)
  18. let is = false,
  19. rect = block.rect,
  20. { width, height } = Game.bounds
  21. if(rect.x < 0) rect.x = 0, is = true
  22. if(rect.x + rect.width > width) rect.x = width - rect.width, is = true
  23. if(rect.y + rect.height > height) rect.y = height - rect.height, is = true
  24. if(rect.y < 0) rect.y = 0, is = true
  25. if(is) rect.update()
  26. return block
  27. }
  28. }
  29. // 边界反弹
  30. export function BoundsRebound(block) {
  31. let _next = block.next
  32. block.next = () => {
  33. _next.call(block)
  34. let is = false,
  35. rect = block.rect,
  36. { width, height } = Game.bounds
  37. if(rect.x < 0) rect.x = 0, block.vx *= -1, is = true
  38. if(rect.y < 0) rect.y = 0, block.vy *= -1, is = true
  39. if(rect.x + rect.width > width) rect.x = width - rect.width, block.vx *= -1, is = true
  40. if(rect.y + rect.height > height) rect.y = height - rect.height, block.vy *= -1, is = true
  41. if(is) rect.update()
  42. return block
  43. }
  44. }
  45. /* 实例装饰 */
  46. // block添加指针
  47. export function pointerExpansion(block, className) {
  48. block.pointer = new Pointer(block, className)
  49. let _init = block.init
  50. block.init = () => {
  51. block.pointer.init()
  52. _init.call(block)
  53. }
  54. let _birth = block.birth
  55. block.birth = (hasAni = true, cb) => {
  56. let _cb = cb
  57. _birth.call(block, hasAni, () => {
  58. block.pointer.birth()
  59. _cb && _cb()
  60. })
  61. }
  62. let _update = block.update
  63. block.update = () => {
  64. block.pointer.update()
  65. _update.call(block)
  66. }
  67. let _destroy = block.destroy
  68. block.destroy = () => {
  69. block.pointer.destroy()
  70. _destroy.call(block)
  71. }
  72. }
  1. //src\script\class\Control.js
  2. //控制鼠标移动
  3. import Rect from './Rect'
  4. import Game from './Game'
  5. /**
  6. * 用户控制器,监听键盘事件,鼠标事件等
  7. */
  8. class Control {
  9. disabled = [] // 禁用按键数组
  10. disableMouse = false // 禁用鼠标监听
  11. keyEvt = {
  12. // key: [fn, fn, fn]
  13. }
  14. OL = 0 // root容器与浏览器左边距离
  15. OT = 0 // root容器与浏览器顶边距离
  16. // 方向键监听使用
  17. count = 0
  18. x = 0
  19. y = 0
  20. deg = {
  21. 'v00': 0,
  22. 'v10': 0,
  23. 'v1-1': 45,
  24. 'v0-1': 90,
  25. 'v-1-1': 135,
  26. 'v-10': 180,
  27. 'v11': -45,
  28. 'v01': -90,
  29. 'v-11': -135
  30. }
  31. constructor() {
  32. window.onresize = () => {
  33. let { left, top } = Game.domRoot.getBoundingClientRect()
  34. this.OL = left + 5
  35. this.OT = top + 5
  36. }
  37. window.onresize()
  38. document.onkeydown = this._down_interface.bind(this)
  39. document.onkeyup = this._up_interface.bind(this)
  40. document.onmousemove = this._mouse_interface.bind(this)
  41. // 监听方向键
  42. this.onKeyDown(87, () => this.dirKeyDown(1))
  43. this.onKeyDown(65, () => this.dirKeyDown(4))
  44. this.onKeyDown(68, () => this.dirKeyDown(2))
  45. this.onKeyDown(83, () => this.dirKeyDown(3))
  46. this.onKeyUp(87, () => this.dirKeyUp(1))
  47. this.onKeyUp(65, () => this.dirKeyUp(4))
  48. this.onKeyUp(68, () => this.dirKeyUp(2))
  49. this.onKeyUp(83, () => this.dirKeyUp(3))
  50. }
  51. // keydown接口
  52. _down_interface(evt) {
  53. let code = evt.keyCode
  54. if(this.disabled.find(v => v == code)) return
  55. code = 'k' + code
  56. let evtArr = this.keyEvt[code]
  57. if(evtArr) {
  58. evtArr.forEach(v => v())
  59. }
  60. }
  61. // keyup接口
  62. _up_interface(evt) {
  63. let code = evt.keyCode
  64. if(this.disabled.find(v => v == code)) return
  65. code = 'c' + code
  66. let evtArr = this.keyEvt[code]
  67. if(evtArr) {
  68. evtArr.forEach(v => v())
  69. }
  70. }
  71. // 鼠标接口
  72. _mouse_interface(evt) {
  73. if(this.disableMouse) return
  74. this.mouseX = evt.pageX - this.OL
  75. this.mouseY = evt.pageY - this.OT
  76. let evtArr = this.keyEvt['mouseMove']
  77. if(evtArr) {
  78. evtArr.forEach(v => v(this.mouseX, this.mouseY))
  79. }
  80. }
  81. // 方向键监听 & 控制
  82. dirReset = () => {
  83. this.count = this.x = this.y = 0
  84. this.dirTrigger(this.count, undefined)
  85. }
  86. dirKeyDown = (key) => {
  87. if(key == 1) {
  88. if(this.y == 0) this.count++
  89. this.y = 1
  90. } else if(key == 2) {
  91. if(this.x == 0) this.count++
  92. this.x = 1
  93. } else if(key == 3) {
  94. if(this.y == 0) this.count++
  95. this.y = -1
  96. } else {
  97. if(this.x == 0) this.count++
  98. this.x = -1
  99. }
  100. this.dirTrigger(this.count, this.deg['v' + this.x + this.y])
  101. }
  102. dirKeyUp = (key) => {
  103. if(key == 1 && this.y == 1 || key == 3 && this.y == -1) {
  104. this.y = 0
  105. this.count--
  106. } else if(key == 2 && this.x == 1 || key == 4 && this.x == -1) {
  107. this.x = 0
  108. this.count--
  109. }
  110. this.dirTrigger(this.count, this.deg['v' + this.x + this.y])
  111. }
  112. dirTrigger = (count, deg) => {
  113. let evtArr = this.keyEvt['dirChange']
  114. if(evtArr) {
  115. evtArr.forEach(v => v(count, deg))
  116. }
  117. }
  118. // 禁用方向键监听
  119. disableDirectionKey() {
  120. this.disabled = this.disabled.concat(['87', '65', '68', '83'])
  121. this.dirReset()
  122. }
  123. // 启用方向键监听
  124. enableDirectionKey() {
  125. this.disabled = this.disabled.filter(v => v != 87 && v != 65 && v != 68 && v != 83)
  126. this.dirReset()
  127. }
  128. /**
  129. * 注册方向键变化事件,当方向键变化时,调用回调函数,传入当前按下的方向键数和方向(deg)
  130. * @param {function(count:number, dir:number)} fn - 回调函数
  131. */
  132. onDirChange(fn) {
  133. let key = 'dirChange'
  134. if(this.keyEvt[key]) {
  135. this.keyEvt[key].push(fn)
  136. } else {
  137. this.keyEvt[key] = [fn]
  138. }
  139. }
  140. /**
  141. * 注册按下键事件
  142. * @param {string|number} key - 键值
  143. * @param {function} fn - 回调函数
  144. */
  145. onKeyDown(key, fn) {
  146. key = 'k' + key
  147. if(this.keyEvt[key]) {
  148. this.keyEvt[key].push(fn)
  149. } else {
  150. this.keyEvt[key] = [fn]
  151. }
  152. }
  153. /**
  154. * 注册松开键事件
  155. * @param {string|number} key - 键值
  156. * @param {function} fn - 回调函数
  157. */
  158. onKeyUp(key, fn) {
  159. key = 'c' + key
  160. if(this.keyEvt[key]) {
  161. this.keyEvt[key].push(fn)
  162. } else {
  163. this.keyEvt[key] = [fn]
  164. }
  165. }
  166. /**
  167. * 注册鼠标移动事件
  168. * @param {function(number:x, number:y)} fn - 回调函数
  169. */
  170. onMouseMove(fn) {
  171. let key = 'mouseMove'
  172. if(this.keyEvt[key]) {
  173. this.keyEvt[key].push(fn)
  174. } else {
  175. this.keyEvt[key] = [fn]
  176. }
  177. }
  178. }
  179. export default Control
  1. //点到另一个点的角度
  2. //src\script\math.js
  3. let { atan2 } = Math,
  4. tmp2 = 180 / Math.PI
  5. // 点到另一个点的角度
  6. export function pointDeg(x1, y1, x2, y2) {
  7. return atan2(y2 - y1, x2 - x1) * tmp2
  8. }
  1. //src\script\class\Block\Unit\Factory.js
  2. let context = require.context('./', false, /\.js$/),
  3. all = context.keys().filter(item => item !== './Factory.js').map(key => context(key)),
  4. map = {}
  5. all.forEach(v => {
  6. map[v.default.FactoryName] = v.default
  7. })
  8. export default function (name, x, y) {
  9. return new map[name](x, y)
  10. }
  1. //src\script\Data\index.js
  2. import { getFileName } from '../utils'
  3. let ctx = require.context('./', false, /\.js$/),
  4. all = ctx.keys().filter(item => item != './index.js' && item != './utils.js').map(key => {
  5. return {
  6. name: getFileName(key),
  7. value: ctx(key).default
  8. }
  9. }),
  10. map = {}
  11. all.forEach(v => { map[v.name] = v.value})
  12. export default map
  1. //src\script\class\QuadTree.js
  2. /*!
  3. 该部分代码参考以下文章
  4. 作者:lxjwlt
  5. 链接:http://blog.lxjwlt.com/front-end/2014/09/04/quadtree-for-collide-detection.html
  6. 來源:个人博客
  7. */
  8. import Rect from './Rect'
  9. /**
  10. * 四叉树对象,用于碰撞检测
  11. * @property {Rect[]} objects - 保存在该节点本身的物体对象
  12. * @property {QuadTree[]} nodes - 子节点
  13. * @property {Rect} bounds - 该节点矩形范围
  14. */
  15. class QuadTree {
  16. // 每个节点最大物体数量
  17. static MAX_OBJECTS = 7
  18. // 判断矩形是否在象限范围内
  19. static isInner = function (rect, bound) {
  20. return rect.x >= bound.x &&
  21. rect.x + rect.width <= bound.x + bound.width &&
  22. rect.y >= bound.y &&
  23. rect.y + rect.height <= bound.y + bound.height
  24. }
  25. /**
  26. * @constructor
  27. * @param {Rect} rect - 边界对象
  28. */
  29. constructor(rect) {
  30. this.objects = []
  31. this.nodes = []
  32. this.bounds = rect
  33. }
  34. /**
  35. * 判断物体属于哪个象限
  36. * @param {Rect} rect - 需要判断的矩形
  37. * @return {number}
  38. * 0 - 象限一
  39. * 1 - 象限二
  40. * 2 - 象限三
  41. * 3 - 象限四
  42. * -1 - 物体跨越多个象限
  43. */
  44. getIndex(rect) {
  45. let bounds = this.bounds,
  46. onTop = rect.y + rect.height <= bounds.centerY,
  47. onBottom = rect.y >= bounds.centerY,
  48. onLeft = rect.x + rect.width <= bounds.centerX,
  49. onRight = rect.x >= bounds.centerX
  50. if(onTop) {
  51. if(onRight) {
  52. return 0
  53. } else if(onLeft) {
  54. return 1
  55. }
  56. } else if(onBottom) {
  57. if(onLeft) {
  58. return 2
  59. } else if(onRight) {
  60. return 3
  61. }
  62. }
  63. return -1
  64. }
  65. /**
  66. * 划分为4个子象限
  67. */
  68. split() {
  69. let bounds = this.bounds,
  70. x = bounds.x,
  71. y = bounds.y,
  72. sWidth = bounds.width / 2,
  73. sHeight = bounds.height / 2
  74. this.nodes.push(
  75. new QuadTree(new Rect(bounds.centerX, y, sWidth, sHeight)),
  76. new QuadTree(new Rect(x, y, sWidth, sHeight)),
  77. new QuadTree(new Rect(x, bounds.centerY, sWidth, sHeight)),
  78. new QuadTree(new Rect(bounds.centerX, bounds.centerY, sWidth, sHeight))
  79. )
  80. }
  81. /**
  82. * 插入物体
  83. * @param {Rect} rect - 需要插入的物体
  84. *
  85. * - 如果当前节点存在子节点,则检查物体到底属于哪个子节点,
  86. * 如果能匹配到子节点,则将该物体插入到该子节点中,否则保存在节点自身
  87. *
  88. * - 如果当前节点不存在子节点,将该物体存储在当前节点。
  89. * 随后,检查当前节点的存储数量,如果超过了最大存储数量,则对当前节点进行划分,
  90. * 划分完成后,将当前节点存储的物体重新分配到四个子节点中。
  91. */
  92. insert(rect) {
  93. let objects = this.objects,
  94. i, index
  95. // 如果该节点下存在子节点
  96. if(this.nodes.length) {
  97. index = this.getIndex(rect)
  98. if(index !== -1) {
  99. this.nodes[index].insert(rect)
  100. return
  101. }
  102. }
  103. // 否则存储在当前节点下
  104. objects.push(rect)
  105. // 如果当前节点没有分裂过 并且 存储的数量超过了MAX_OBJECTS
  106. if(!this.nodes.length && this.objects.length > QuadTree.MAX_OBJECTS) {
  107. this.split()
  108. for(i = objects.length - 1; i >= 0; i--) {
  109. index = this.getIndex(objects[i])
  110. if(index !== -1) {
  111. this.nodes[index].insert(objects.splice(i, 1)[0])
  112. }
  113. }
  114. }
  115. }
  116. /**
  117. * 检索功能:
  118. * 给出一个物体对象,将该物体可能发生碰撞的所有物体选取出来。
  119. * 该函数先查找物体所属的象限,该象限下的物体都是有可能发生碰撞的,然后再递归地查找子象限。
  120. * @param {Rect} rect - 需要检索的矩形对象
  121. * @return {Rect[]}
  122. */
  123. retrieve(rect) {
  124. let result = [],
  125. arr, i, index
  126. if(this.nodes.length) {
  127. index = this.getIndex(rect)
  128. if(index !== -1) {
  129. result = result.concat(this.nodes[index].retrieve(rect))
  130. } else {
  131. // 切割矩形
  132. arr = rect.carve(this.bounds.centerX, this.bounds.centerY)
  133. for(i = arr.length - 1; i >= 0; i--) {
  134. index = this.getIndex(arr[i])
  135. result = result.concat(this.nodes[index].retrieve(rect))
  136. }
  137. }
  138. }
  139. result = result.concat(this.objects)
  140. return result
  141. }
  142. /**
  143. * 移除目标矩形对象,如果在自身没有找到,则递归查找子象限
  144. * @param {Rect} rect - 要删除伯矩形对象
  145. * @return {boolean} 是否成功删除目标对象
  146. */
  147. remove(rect) {
  148. let objects = this.objects,
  149. nodes = this.nodes
  150. let target = objects.findIndex(v => v === rect)
  151. if(target !== -1) {
  152. objects.splice(target, 1)
  153. return true
  154. } else if(nodes.length) {
  155. for(let i = 0; i < nodes.length; i++) {
  156. let node = nodes[i]
  157. if(node.remove(rect)) {
  158. return true
  159. }
  160. }
  161. }
  162. return false
  163. }
  164. /**
  165. * 动态刷新
  166. * 从根节点深入四叉树,检查四叉树各个节点存储的物体是否依旧属于该节点(象限)的范围之内,如果不属于,则重新插入该物体。
  167. * @param {QuadTree} root - 当前检索的节点对象
  168. */
  169. refresh(root = this) {
  170. let objects = this.objects,
  171. rect, index, i, len
  172. for(i = objects.length - 1; i >= 0; i--) {
  173. rect = objects[i]
  174. index = this.getIndex(rect)
  175. // 如果矩形不属于该象限,则将该矩形重新插入
  176. if(!QuadTree.isInner(rect, this.bounds)) {
  177. if(this !== root) {
  178. root.insert(objects.splice(i, 1)[0])
  179. }
  180. // 如果矩形属于该象限 且 该象限具有子象限,则
  181. // 将该矩形安插到子象限中
  182. } else if(this.nodes.length && index !== -1) {
  183. this.nodes[index].insert(objects.splice(i, 1)[0])
  184. }
  185. }
  186. // 递归刷新子象限
  187. for(i = 0, len = this.nodes.length; i < len; i++) {
  188. this.nodes[i].refresh(root)
  189. }
  190. }
  191. }
  192. export default QuadTree

【默默努力】PixelFire的更多相关文章

  1. 2018.5.2(7:20到的办公室开始早课 阮一峰的JS) 所有的默默努力都是为了让自己看起来毫不费力

    continue语句用于立即终止本轮循环,返回循环结构的头部,开始下一轮循环. break语句用于跳出代码块或循环. 标签(label) JavaScript 语言允许,语句的前面有标签(label) ...

  2. 【默默努力】fishingGame

    这个捕鱼游戏挺有意思的,通过发射子弹,打鱼.打鱼的子弹会消耗金币,但是打鱼如果打到了鱼,就会奖励金币的数量. 我如果写这个的话,应该会画一个 背景海底,然后生成很多鱼的图片,还要有一个大炮,金币.大炮 ...

  3. 【默默努力】h5-game-heroVSmonster

    先放下作者大大的项目地址:https://github.com/yangyunhe369/h5-game-heroVSmonster 然后游戏的效果为 截动图的按键与游戏按键应该冲突,我就截几张图片了 ...

  4. 【默默努力】h5-game-blockBreaker

    先放下游戏的效果,我不太会玩游戏 然后放下无私开源的作者大大的地址:https://github.com/yangyunhe369/h5-game-blockBreaker 这个游戏的话,我觉得应该是 ...

  5. 【默默努力】ig-wxz-and-hotdog

    这个是一个非常跟热点的小游戏,思聪吃热狗.这个游戏的话,我感觉思路还挺简单的,天上会掉热狗和障碍物, 思聪在下面张开嘴巴,进行左右移动,接热狗.如果吃到的是热狗就得一分,如果思聪吃到的不是热狗,是障碍 ...

  6. 【默默努力】react-drag-grid

    先放项目地址:https://github.com/Bilif/react-drag-grid 项目运行效果 感谢无私开源的程序员 先看项目入口文件 //index.js import React f ...

  7. 【默默努力】vue-pc-app

    最近在github上面看到了一个团队的项目,真的非常赞.他们进行vue-cli的二次开发,将项目用自己的方式打包. 今天的这个开源项目地址为:https://github.com/tffe-team/ ...

  8. Shader的学习方法总结

    最近网友candycat1992的新书<Unity Shader入门精要>出版了,估计万千的中国unity开发者又要掀起一波学Shader热潮了.我也想把自己这几年学习Shader的一些历 ...

  9. VIM移动

    VIM移动   断断续续的使用VIM也一年了,会的始终都是那么几个命令,效率极低 前几个星期把Windows换成了Linux Mint,基本上也稳定了下来 就今晚,我已经下定决心开始新的VIM之旅,顺 ...

随机推荐

  1. NX二次开发-UFUN求对象的最大边界框UF_MODL_ask_bounding_box

    NX9+VS2012 #include <uf.h> #include <uf_obj.h> #include <uf_modl.h> #include <u ...

  2. NX二次开发-设置功能区工具栏的可见性UF_UI_set_ribbon_vis

    NX9+VS2012 1.打开D:\Program Files\Siemens\NX 9.0\UGII\menus\ug_main.men 找到装配和PMI,在中间加上一段 TOGGLE_BUTTON ...

  3. sqlserver 调优(二)

    良好的系统和数据库设计,优质的SQL编写,合适的数据表索引设计,甚至各种硬件因素:网络性能.服务器的性能.操作系统的性能,甚至网卡.交换机等.这篇文章主要讲到如何改善SQL语句,还将有另一篇讨论如何改 ...

  4. spring boot读取自定义配置文件时乱码解决办法

    @PropertySource(value = "classpath:book.yml", ignoreResourceNotFound = true,encoding = &qu ...

  5. git学习记录2(远程库管理)

    学习参考地址:https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000 本编随笔只是自己对 ...

  6. USACO2008 Cow Cars /// oj23323

    题目大意: N (1 ≤ N ≤ 50,000)头牛被编号为1-N,牛i可以在M(1 ≤ M ≤ N)条不同的高速路上以Si (1 ≤ Si ≤ 1,000,000) km/h的速度飞驰 为了避免相撞 ...

  7. 2018湘潭大学程序设计竞赛【H】

    题目链接:https://www.nowcoder.com/acm/contest/105/H 题意:两个操作,一个在[l,r]区间放颜色为c的球,一个统计在[l,r]里有多少不同颜色的球. 题解:哎 ...

  8. 实时收集Storm日志到ELK集群

    背景 我们的storm实时流计算项目已经上线几个月了,由于各种原因迟迟没有进行监控,每次出现问题都要登录好几台机器,然后使用sed,shell,awk,vi等各种命令来查询原因,效率非常低下,而且有些 ...

  9. centos安装与配置R语言

    Linux下安装R语言 一.编译安装 由于采用编译安装,所以需要用到gcc编译环境,在编译前check文件时还会用到libXt-devel和readline-devel两个依赖,所以在编译R语言源码时 ...

  10. protobuf文档翻译-安装,数据格式及编码规范

    Install Download protobuf: https://github.com/protocolbuffers/protobuf/releases unzip protoc-3.8.0-l ...