简单对Hilojs封装一下,以前练手小示例
- 俄罗斯方块
- 消方块
- 热气球,这个是外网上的觉得画风有意思,背景全是canvas绘的,我是用hilojs写的,写了一半
var PluginManager = Hilo.Class.create({
Mixes: Hilo.EventMixin,
Statics: {
plugins: [],
register: function (plugin) {
this.plugins.push(plugin)
}
},
constructor: function (game) {
this.options = game.options.pluginConfig || {}
this.game = game
this.plugins = PluginManager.plugins.concat(game.options.plugins)
this.game.on('init', () => {
this.callHook('init')
})
this.game.on('destroy', () => {
this.callHook('destroy')
this.plugins.length = 0
})
this.callHook('create')
},
callHook(method) {
let plugins = this.plugins, len = plugins.length
for (let i = 0; i < len; i++) {
let plugin = plugins[i]
if (plugin[method]) {
plugin[method].call(this, this.game, this.options)
}
}
}
})
let BootPlugin = Hilo.Class.create({
Statics: {
name: 'Boot',
create(game) {
this.boot = new BootPlugin(game)
}
},
constructor: function (game) {
this.game = game
}
})
var Asset = Hilo.Class.create({
Mixes: Hilo.EventMixin,
queue: null,
maxConnections: 2,
constructor: function (game) {
this.game = game
this.wrapper = Hilo.createElement('div', {
style: {
display: "flex",
alignItems: "center",
justifyContent: "center",
width: '100%',
height: '100%'
}
})
this.progressWrapper = Hilo.createElement('div', {
style: {
position: "relative",
display: "flex",
alignItems: "center",
justifyContent: "center",
backgroundColor: '#dff5dd',
width: '80%',
height: '30px'
}
})
this.progressText = Hilo.createElement('span', {
style: {
position: "relative",
'font-size': '12px',
'color': '#152b1c',
'font-family': 'sans-serif,helvetica',
zIndex: 2
},
innerText: "0%"
})
this.progressBG = Hilo.createElement('div', {
style: {
position: "absolute",
backgroundColor: 'rgb(243 109 6)',
left: 0,
top: 0,
width: '0%',
height: '100%',
zIndex: 1
}
})
this.progressWrapper.appendChild(this.progressText)
this.progressWrapper.appendChild(this.progressBG)
this.wrapper.appendChild(this.progressWrapper)
this.game.container.appendChild(this.wrapper)
// 创建加载队列
this.queue = new Hilo.LoadQueue()
this.queue.maxConnections = this.maxConnections;
this.queue.add(this.game.options.resources)
this.queue.on('load', (item) => {
let loaded = this.queue.getLoaded()
let total = this.queue.getTotal()
let percent = Number(loaded / total * 100)
this.progressText.innerText = percent + '%'
this.progressBG.style.width = percent + '%'
})
this.queue.on('complete', this.onComplete.bind(this), true)
},
getContent(id) {
return this.queue.getContent(id)
},
load() {
this.queue.start()
},
onComplete() {
this.game.container.removeChild(this.wrapper)
this.queue.off('load')
this.fire('complete')
}
})
var Fish = Hilo.Class.create({
Extends: Hilo.Sprite,
Mixes: Hilo.EventMixin,
constructor: function (name) {
this.name = name;
}
})
var Scene = Hilo.Class.create({
scene: null,
game: null,
stage: null,
asset: null,
root: null,
name: null,
constructor: function (properties) {
properties = properties || {}
Hilo.util.copy(this, properties, false)
this.isActive = false // 未激活
// Hilo.util.copy(this, properties, true)
},
initialize: function () {
this.root = new Hilo.Container({
onUpdate: this.onShouldUpdate.bind(this)
}).addTo(this.scene.container)
},
init: function () { },
create: function () { },
update: function () { },
add: function (obj) {
this.root.addChild(obj)
return this
},
onShouldUpdate: function () {
return this.isActive
},
destroy: function () {
this.scene.container.removeChild(this.root)
this.root = null
this.isActive = false
this.isInitialize = false
}
})
var SceneManager = Hilo.Class.create({
game: null,
index: 0,
constructor: function (game) {
this.game = game
this.scenes = []
this.currentScene = null
let optScenes = game.options.scenes
this.container = new Hilo.Container().addTo(game.stage)
for (let i = 0; i < optScenes.length; i++) {
let Scene = optScenes[i]
let scene = this.createSceneInstance(Scene, i)
this.scenes.push(scene)
}
this.game.on('init', () => {
if (this.scenes.length) {
this.startScene(this.index)
}
})
this.game.on('tick', this.update.bind(this))
},
createSceneInstance: function (SceneObj) {
let scene;
if (SceneObj.superclass && SceneObj.superclass.constructor === Scene) {
scene = new SceneObj()
} else {
scene = new Scene()
Object.assign(scene, SceneObj);
}
scene.scene = this
scene.game = this.game
scene.stage = this.game.stage
scene.asset = this.game.asset
return scene
},
getScene: function (index) {
return this.scenes[index]
},
startScene: function (index) {
this.index = index
let scene = this.getScene(index)
if (scene) {
if (!scene.isActive) {
scene.isActive = true
scene.initialize()
scene.init()
scene.create()
}
}
},
switchScene: function (index) {
let currentScene = this.getScene(this.index)
let scene = this.getScene(index)
if (this.currentScene == scene) {
return
}
if (currentScene) {
currentScene.destroy()
}
this.startScene(index)
},
update: function (e) {
let scenes = this.scenes, scene
for (let i = 0, len = scenes.length; i < len; i++) {
scene = scenes[i]
if (scene.isActive) {
scene.update(e.detail)
}
}
}
})
function random(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
var Game = Hilo.Class.create({
Mixes: Hilo.EventMixin,
stage: null,
world: null,
container: null,
isReady: false,
constructor: function (options) {
this.options = {
container: null,
width: 600,
height: 600,
scaleX: 1,
scaleY: 1,
dpr: 1,
renderType: "canvas",
background: null,
ticker: null,
startTick: true,
fps: 60,
resources: [],
plugins: [],
pluginConfig: {},
scenes: [],
interactiveEvents: ["mousedown", "mousemove", "mouseup"],
enableDOMEvent: false
}
this.tickCallbacks = []
let opts = Hilo.util.copy(this.options, options || {}, true)
Hilo.util.copy(this, _.omit(options, Object.keys(opts)))
this.container = opts.container
this.container.style.position = 'relative'
this.container.style.width = opts.width + 'px'
this.container.style.height = opts.height + 'px'
// this.onShouldUpdate=this.onShouldUpdate.bind(this)
this.stage = new Hilo.Stage({
container: this.container,
width: opts.width,
height: opts.height,
renderType: opts.renderType,
scaleX: opts.scaleX,
scaleY: opts.scaleY,
background: opts.background,
// onUpdate: this.onShouldUpdate
})
let game = this;
if (opts.dpr !== 1) {
this.resize(opts.width, opts.height)
const transform = this.stage.renderer.transform;
this.stage.renderer.transform = function (target) {
transform.call(this, target);
if (target === this.stage) {
this.context.scale(game.options.dpr, game.options.dpr)
}
}
}
Object.defineProperties(this, {
width: {
get: function () {
return this.stage.width
}
},
height: {
get: function () {
return this.stage.height
}
}
})
if (this.options.enableDOMEvent) {
this.stage.enableDOMEvent(this.options.interactiveEvents, true)
}
this.asset = new Asset(this)
this.scene = new SceneManager(this)
this._beforeCreate(this.options)
this._initTicker()
// this.stage.canvas.style.borderWidth = '1px';
// this.stage.canvas.style.borderStyle = "solid";
// this.stage.canvas.style.borderColor = '#000'
// 资源加载完成后,初始化游戏
this.asset.on('complete', () => {
this._init()
}, true)
this.pluginManager = new PluginManager(this)
this._create()
// 在下次宏执行,避免load执行期间,onComplete被执行
setTimeout(() => {
this.asset.load()
})
},
resize(width, height) {
this.stage.width = width
this.stage.height = height
this.stage.renderer.resize.call({
canvas: this.stage.canvas,
stage: {
width: width,
height: height,
scaleX: 1,
scaleY: 1
}
}, width * this.options.dpr, height * this.options.dpr)
this.stage.updateViewport()
},
getMouseEvent(e) {
let rect = this.container.getBoundingClientRect();
return { x: e.clientX - rect.left, y: e.clientY - rect.top }
},
_initTicker() {
//启动计时器
this.ticker = new Hilo.Ticker(this.options.fps);
// this.ticker.addTick(Hilo.Tween);
this.ticker.addTick(this);
// this.ticker.addTick(this.stage);
if (this.options.startTick) {
this.ticker.start(true);
}
},
initPhysicsWorld(physicsCfg) {
var opts = Object.assign({
gravity: {
x: 0,
y: 300
},
}, physicsCfg || {})
var world = new Hilo.Physics({
x: opts.gravity.x,
y: opts.gravity.y
}, {
// iterations:10,
sleepTimeThreshold: Infinity
});
this.world = world;
},
handleShouldUpdate(delta) {
if (this.onShouldUpdate) {
return this.onShouldUpdate(delta)
}
},
tick(delta) {
//this.onUpdate(delta)
// console.log('delta',delta)
if (this.world) {
this.world.tick(delta);
}
this.fire('tick', delta)
Hilo.Tween.tick(delta)
if (!this.onShouldUpdate || this.onShouldUpdate(delta) !== false) {
this.stage.tick(delta)
}
},
update(delta) {
if (!this.options.startTick) {
this.stage.tick(delta || this.ticker_interval)
}
},
_beforeCreate(options) {
this.beforeCreate(options)
this.fire('beforeCreate', options)
},
beforeCreate(options) { },
_create() {
this.create()
this.fire('create')
},
create() { },
_init() {
this.isReady = true
this.init()
this.fire('init')
},
init() { },
destroy() {
}
})
addExample("热气球", function () {
return {
template: `<div><div ref="main"></div></div>`,
data() { return {}; },
computed: {},
methods: {},
mounted() {
var container = this.$refs.main;
var stageWidth = 600, stageHeight = 600;
var mapWidth = stageWidth * 1, mapHeight = stageHeight
let map = new Hilo.Container()
let groundHeight = 200;
let PlayScene = Hilo.Class.create({
Extends: Scene,
gravityY: 9.8,
damping: 0.9,
physicsBodies: [],
isStartGame: false,
down: false,
scrollX: 0,
offsetX: 250,
hills: [],
isOver: false,
constructor: function () {
PlayScene.superclass.constructor.call(this, { name: 'Play' });
},
bindPhysics(obj, properties) {
Object.assign(obj, {
vx: 0,
vy: 0,
mass: 1,
forceX: 0,
forceY: 0
}, properties || {});
// obj.updatePosition=this.updatePosition.bind(this,obj)
this.physicsBodies.push(obj)
},
updatePosition(obj, delta) {
let ax = 0;
let ay = (this.gravityY + obj.forceY / obj.mass) * delta
obj.vx = obj.vx + ax;
obj.vy = obj.vy + ay;
obj.x += obj.vx;
obj.y += obj.vy;
if (obj.y > 0) {
obj.y = 0
obj.vy = 0;
}
obj.forceY = 0;
},
update(delta) {
if (!this.isStartGame) {
return
}
if (this.down) {
this.balloon.forceY = -20;
}
let physicsBodies = this.physicsBodies;
for (let i = 0, len = physicsBodies.length; i < len; i++) {
this.updatePosition(physicsBodies[i], delta * 0.001)
}
this.scrollX = this.balloon.x - this.offsetX;
this.root.x = -this.scrollX;
this.drawTrees(this.balloon.x)
this.drawHills(this.balloon.x)
// if(this.balloon.hitTestObject())
},
create() {
const bgContainer = new Hilo.Container()
this.stage.addChildAt(bgContainer, 0)
this.bgContainer = bgContainer
this.trees = []
this.root.x = this.offsetX;
this.root.y = stageHeight - groundHeight;
this.balloon = this.createBalloon()
this.createBackgroundSky()
this.createHill('#88aa22', groundHeight + 80, 0.2, 20, 1);
this.createHill('#778800', groundHeight + 60, 0.2, 15, 0.5);
this.createHill('#26532B', groundHeight + 40, 0.2, 10, 0.2);
this.drawHills(0)
this.bindPhysics(this.balloon, { vx: 3 })
this.drawTrees(this.balloon.x)
this.stage.on('mousedown', () => {
this.isStartGame = true;
this.down = true
})
this.stage.on('mouseup', () => {
this.down = false
})
},
getHillY(x, offset, baseHeight, speedMultiplier, amplitude, stretch) {
const sineBaseY = -baseHeight;
return (
Math.sin((offset * speedMultiplier + x) * stretch / 180 * Math.PI) * amplitude +
sineBaseY
);
},
drawHill(hill, color, offset, baseHeight, speedMultiplier, amplitude, stretch) {
hill.clear()
hill.y = stageHeight
hill.beginPath()
hill.beginFill(color)
hill.moveTo(0, 0)
hill.lineTo(0, this.getHillY(0, offset, baseHeight, speedMultiplier, amplitude, stretch))
for (let i = 0; i <= stageWidth; i++) {
let x = i;
let y = this.getHillY(i, offset, baseHeight, speedMultiplier, amplitude, stretch)
hill.lineTo(x, y)
}
hill.lineTo(stageWidth, 0)
hill.closePath()
hill.endFill()
},
drawHills(offset) {
this.hills.forEach(hill => {
let [color, baseHeight, speedMultiplier, amplitude, stretch] = hill.args
this.drawHill(hill.obj, color, offset, baseHeight, speedMultiplier, amplitude, stretch)
})
},
createHill(color, baseHeight, speedMultiplier, amplitude, stretch) {
const hill = new Hilo.Graphics();
this.hills.push({
obj: hill,
args: [color, baseHeight, speedMultiplier, amplitude, stretch]
})
this.bgContainer.addChild(hill);
},
createBackgroundSky() {
// 背景天空
const bg = new Hilo.Graphics();
bg.beginPath()
bg.beginLinearGradientFill(stageWidth / 2, 0, stageWidth / 2, stageHeight, ['#AADBEA', '#FEF1E1'], [0, 1])
bg.drawRect(0, 0, stageWidth, stageHeight)
bg.endFill()
this.game.stage.addChildAt(bg, 0);
},
drawTrees: function (offset) {
if (this.trees.length && this.trees[0].x - this.scrollX < -100) {
this.trees[0].removeFromParent()
this.trees.shift();
}
const minGap = 100, maxGap = 500;
for (let i = this.trees.length; i < 10; i++) {
let gap = random(minGap, maxGap);
let x = i == 0 ? 400 : gap + this.trees[i - 1].x;
this.trees.push(this.generateRandomTree(x))
}
},
generateRandomTree(offset) {
const treeW = [40, 50, 60]
const treeH = [70, 100, 80]
const treeColor = ['#88aa22', '#667711', '#779933']
const index = random(0, treeW.length);
let radius = treeW[index];
let height = treeH[index];
let color = treeColor[index];
let x = offset
let tree = this.createTree(x, 0, radius, height, color)
return tree
},
createTree(x, y, radius, height, shuYeColor) {
const realRadius = radius * 0.8
const padding = radius - realRadius
const w = Math.floor(realRadius * 0.5);
const pi2 = Math.PI * 2
const tree = new Hilo.Graphics();
tree.x = x;
tree.y = y;
tree.beginFill('#881100');
tree.moveTo(-w, 0)
tree.quadraticCurveTo(-w + 10, -height / 2, -w, -height)
tree.lineTo(w, -height);
tree.quadraticCurveTo(w - 10, -height / 2, w, 0)
tree.closePath()
tree.endFill()
const xlist = Array.from({ length: 7 }, () => {
let p = Math.random() * 2 - 1
let p2 = Math.random() * 2 - 1
return [Math.floor(p * padding), Math.floor(p2 * padding * 1.5)]
})
// 创建树叶
for (let i = 0; i < 7; i++) {
let x = xlist[i][0];
let y = xlist[i][1] - height * 1.3;
tree.beginPath()
tree.beginFill(shuYeColor);
tree._addAction(['arc', x, y, realRadius, pi2, false])
tree.endFill()
}
// 树干
this.add(tree);
return tree
},
createBalloon(x = 0, y = 0) {
// 篮子
const basket = new Hilo.Graphics();
basket.x = x;
basket.y = y;
basket.beginPath();
basket.beginFill('#ff3333');
// basket.drawCircle(-10,-10,10)
basket.drawRect(-30, -50, 60, 10)
basket.endFill()
basket.beginPath();
basket.beginFill('#ff8888');
basket.drawRect(-30, -40, 60, 40)
basket.endFill()
// 绳子
basket.beginPath();
basket.beginFill('#ff3333');
basket.drawRect(-27, -80, 2, 30)
basket.endFill()
basket.beginPath();
basket.beginFill('#ff3333');
basket.drawRect(26, -80, 2, 30)
basket.endFill()
// 气球
basket.beginPath();
basket.beginFill('#ff0000');
// basket.hasFill=false;
// basket.lineStyle(1,'#ff0000')
basket.moveTo(-30, -80);
basket.quadraticCurveTo(-80, -140, -80, -180)
basket._addAction(['arc', 0, -180, 80, Math.PI, false])
basket.quadraticCurveTo(80, -140, 30, -80)
basket.closePath();
basket.endFill()
this.add(basket)
return basket;
}
})
let game = new Game({
container: container,
width: stageWidth,
height: stageHeight,
background: '#ddd',
enableDOMEvent: true,
// scaleX: window.devicePixelRatio,
// scaleY: window.devicePixelRatio,
scenes: [PlayScene],
init() {
}
});
game.stage.addChild(map)
game.on('tick', (dt) => {
// camera.tick(dt)
// map.x = -camera.scroll.x;
// console.log('map', map.x)
})
}
}
})
addExample("消方块", function () {
return {
template: `<div><div style="margin:50px auto" ref="main"></div></div>`,
data() { return {}; },
computed: {},
methods: {},
mounted() {
var container = this.$refs.main;
function randomIndex(min, max) {
return Math.round(Math.random() * (max - min)) + min;
}
function initGame(game) {
}
let blockScene = new Scene({
moveAction: '',
init() {
var game = this.game;
var viewWidth = game.options.width, viewHeight = game.options.height;
var that = this;
this.game.initPhysicsWorld({
gravity: {
x: 0,
y: 0
}
});
var world = this.game.world;
var space = world.space
// world.collisionBias=0
// world.createBounds(viewWidth,viewHeight)
// world._createBound({x:0,y:0},{x:viewWidth,y:0},1)
function createBound(a, b) {
let shape = new cp.SegmentShape(space.staticBody, a, b, 1);
space.addShape(shape);
shape.setElasticity(1);
shape.setFriction(0);
shape.setCollisionType(4);
return shape;
}
function createBounds() {
let left = createBound({
x: 0,
y: 0,
}, {
x: 0,
y: viewHeight
})
let right = createBound({
x: viewWidth,
y: 0,
}, {
x: viewWidth,
y: viewHeight
})
let top = createBound({
x: 0,
y: 0,
}, {
x: viewWidth,
y: 0
})
let bottom = createBound({
x: 0,
y: viewHeight,
}, {
x: viewWidth,
y: viewHeight
})
}
createBounds();
function createBlock(x, y, w, h) {
var colors = ['red', 'green', 'blue'];
var block = new Hilo.Graphics();
var color = colors[randomIndex(0, 2)];
block.x = x + w / 2;
block.y = y + h / 2;
block.width = w;
block.height = h;
block.pivotX = w / 2;
block.pivotY = h / 2;
block.beginFill(color);
block.drawRect(0, 0, w, h);
block.endFill();
world.bindView(block, {
isStatic: true,
type: Hilo.Physics.SHAPE_RECT,
collisionType: 3,
friction: 0,
restitution: 1,
})
return block
}
function createBall(x, y) {
var ball = new Hilo.Graphics();
ball.x = x;
ball.y = y;
ball.pivotX = 20;
ball.pivotY = 20;
ball.width = 40;
ball.height = 40;
ball.beginFill('#fff');
ball.drawCircle(0, 0, 20);
ball.endFill();
world.bindView(ball, {
type: Hilo.Physics.SHAPE_CIRCLE,
collisionType: 1,
radius: 20,
friction: 0,
restitution: 1,
group: 1,
// mass:1
// layers:4
})
ball.shape.surface_v = cp.v(1, 1)
return ball
}
function createPaddle(x, y) {
var ball = new Hilo.Graphics();
ball.x = x;
ball.y = y;
ball.width = 100;
ball.height = 10;
ball.pivotX = 50;
ball.pivotY = 5;
ball.beginFill('#fff');
ball.drawRect(0, 0, 100, 10);
ball.endFill();
world.bindView(ball, {
isStatic: true,
friction: 0,
restitution: 1,
width: 100,
height: 10,
collisionType: 2,
mass: 1
// group:1,
// layers:2
})
return ball
}
function killBlock(block) {
let width = block.width, height = block.height;
let splitCount = 4;
let w = Math.sqrt((width * width / splitCount));
let h = Math.sqrt((height * height / splitCount));
let columnCount = width / w;
let rowCount = height / h;
let blocks = new Hilo.Container();
let cx = block.x;
let cy = block.y;
let x1 = block.x - width / 2;
let y1 = block.y - height / 2;
let dist = 100; // 爆炸飞出距离
let velocityX = 5;
let velocityY = 5;
let speed = 5;
let gravity = 0.98;
let animates = []
for (let r = 0; r < rowCount; r++) {
for (let c = 0; c < columnCount; c++) {
let x = x1 + c * w;
let y = y1 + r * h;
let g = new Hilo.Graphics();
g.beginFill(block.fillStyle);
g.x = x;
g.y = y;
g.drawRect(0, 0, w, h);
g.endFill();
blocks.addChild(g);
// 计算左右两边块的角度
if (c === 0 && (r === 0 || r + 1 === rowCount)) {
let dx = x - cx;
let dy = r === 0 ? y - cy : y + h - cy;
let angle = Math.atan2(dy, dx);
console.log('angle', dx, dy, cx, cy)
// 计算角速度
let vx = Math.cos(angle) * speed;
let vy = Math.sin(angle) * speed;
animates.push({
obj: g,
vx: vx,
vy: vy
})
} else if (c + 1 === columnCount && (r === 0 || r + 1 === rowCount)) {
let dx = x + w - cx;
let dy = r === 0 ? y - cy : y + h - cy;
let angle = Math.atan2(dy, dx);
// 计算角速度
let vx = Math.cos(angle) * speed;
let vy = Math.sin(angle) * speed;
animates.push({
obj: g,
vx: vx,
vy: vy
})
} else if (c === 0) {
animates.push({
obj: g,
vx: c < columnCount * 0.5 ? -speed : speed,
vy: 0
})
} else if (r === 0) {
animates.push({
obj: g,
vx: 0,
vy: r < rowCount * 0.5 ? speed : -speed
})
}
}
}
game.stage.addChild(blocks);
let completeCount = 0
animates.forEach(d => {
let vx = 0;
let vy = 0;
let y = d.obj.y;
vx = d.vx;
vy = d.vy - 5;
Hilo.Tween.fromTo({ x: 1 }, { x: 0 }, null, {
onUpdate: (p) => {
vy = vy + gravity;
y += vy;
d.obj.x += vx;
d.obj.y = y; //-210*Math.sin(p*Math.PI);
},
onComplete: () => {
completeCount++;
if (completeCount === animates.length) {
game.stage.removeChild(blocks);
}
},
duration: 1000
});
})
}
var column = 10, row = 10;
var gap = 5;
var blockWidth = 50
var blockHeight = 20;
var blocks = [];
var blockContainer = new Hilo.Container();
function createBlocks() {
blockContainer.addTo(game.stage)
for (let r = 0; r < row; r++) {
for (let c = 0; c < column; c++) {
let x = c * blockWidth + gap * c + gap;
let y = r * blockHeight + gap * r + gap;
let block = createBlock(x, y, blockWidth, blockHeight);
blockContainer.addChild(block)
}
}
}
createBlocks()
var paddle = createPaddle(viewWidth / 2, viewHeight - 5);
game.stage.addChild(paddle)
var ball = createBall(viewWidth / 2, viewHeight - 30);
game.stage.addChild(ball);
this.paddle = paddle;
this.ball = ball;
var isStart = false;
game.container.addEventListener('pointermove', e => {
let rect = e.currentTarget.getBoundingClientRect();
let x = e.clientX - rect.left, y = e.clientY - rect.top;
paddle.setPosition(x, paddle.y)
if (!isStart) {
ball.setPosition(x, ball.y)
}
})
game.container.addEventListener('pointerup', e => {
if (!isStart) {
ball.body.setVel({ x: -100, y: -200 })
// ball.applyForce({x:-100,y:-200})
// ball.applyImpulse({x:-100,y:-200})
isStart = true;
} else {
if (!game.ticker._paused) {
game.ticker.pause()
} else {
game.ticker.resume()
}
}
})
keyboardJS.bind('space', (e) => {
//this.moveGround();
e.preventDefault()
}, () => {
this.ball.applyImpulse({
x: 350,
y: -500
});
// this.ball.applyForce({
// x:10,
// y:-100
// });
});
keyboardJS.bind('left', () => {
this.moveAction = 'left';
//this.moveGround();
}, () => {
this.moveAction = ''
});
keyboardJS.bind('right', () => {
this.moveAction = 'right';
// this.moveGround();
}, () => {
this.moveAction = ''
});
world.addCollisionListener(1, 3, {
begin(arbiter) {
console.log('碰撞前', arbiter.getA().body.vy)
return true;
},
preSolve(arbiter) {
return true;
},
postSolve(arbiter) {
},
separate(arbiter) {
console.log('碰撞后', arbiter.getA().body.vy)
killBlock(arbiter.body_b.view)
arbiter.body_b.view.removeFromParent();
//console.log('collision',arguments)
world.unbindView(arbiter.body_b.view);
// ball.shape.setElasticity(1)
// ball.applyForce({x:3,y:3}, arbiter.body_b.p)
// let speed = 200;
// let vx = ball.body.vx;
// let vy = ball.body.vy;
// //Math.sqrt(vx*vx+vy*vy)
// let dist = Math.hypot(vx, vy);
// vx = vx / dist * speed;
// vy = vy / dist * speed;
// console.log('vx', vx, vy)
// ball.body.setVel({ x: vx, y: vy })
}
});
let speedX = 10;
world.addCollisionListener(1, 2, {
separate(arbiter) {
if (!isStart) {
return
}
// console.log('paddle collision')
// ball.applyForce({x:10,y:10}, arbiter.body_a.p)
if (ball.x < paddle.x) {
let diff = paddle.x - ball.x;
let x = diff * speedX;
ball.body.setVel({ x: -x, y: ball.body.vy })
} else if (ball.x > paddle.x) {
let diff = ball.x - paddle.x;
let x = diff * speedX;
ball.body.setVel({ x: x, y: ball.body.vy })
} else {
let x = 2 + Math.random() * 8;
ball.body.setVel({ x: x, y: ball.body.vy })
}
}
})
world.addCollisionListener(1, 4, {
separate(arbiter) {
console.log('墙面')
}
})
},
isStart: false,
start() {
//this.isStart = true;
// this.ball.body.setVel({x:150,y:-250})
// this.ball.applyImpulse({
// x:350,
// y:-500
// });
// this.ball.body.setVel({
// x:100,
// y:-200
// })
},
movePaddle() {
let moveAction = this.moveAction;
if (moveAction === 'left') {
let x = this.paddle.x - 10;
this.paddle.setPosition(
Math.max(x, 50),
this.paddle.y
)
} else if (moveAction === 'right') {
let x = this.paddle.x + 10;
this.paddle.setPosition(
Math.min(x, this.game.options.width - 50),
this.paddle.y
)
}
},
moveBall() {
// this.ball.x+=2;
//this.ball.y-=1;
},
update() {
console.log(this.ball.body.vx, this.ball.body.vy)
this.movePaddle();
// var width=this.game.options.width,height=this.game.options.height;
if (this.isStart) {
//if(this.ball.x-20>=this)
// this.moveBall();
}
}
})
let game = new Game({
container: container,
width: 555,
height: 500,
background: '#ddd',
scenes: [blockScene],
init() {
// 初始
initGame(this);
}
});
}
}
})
addExample("俄罗斯方块", function () {
/**
* 所有俄罗斯方块
* 方块标志,从矩阵表示转换为更简单的16进制表示
* 从上到下,从左到右 0x0e00 1,2,4,8
* [0,0,0,0] 0
* [1,1,1,1] 0
* [0,0,0,0] 0
* [0,0,0,0] f
*/
let blockColors = ['red', 'green', 'blue']
// Z l T L
let blocks = [
{
data: [0x4444, 0x00f0] //l
},
// {
// data: [0x0066] //田
// },
// {
// data: [0x0722, 0x0171, 0x0474, 0x0227] // T
// }
]
// 25*10 25*20
return {
template: `
<div>
<div ref="main"></div>
</div>`,
data() { return {}; },
computed: {},
methods: {},
mounted() {
var left = this.$refs.main;// 左边游戏区
var previewEl = this.$refs.preview;// 右边分数
const BLOCK_SIZE = 30, ROW = 16, COLS = 10, LINE_WIDTH = 1;
const MARGIN = [10, 10, 10, 10]//上右下左
const PREVIEW_BLOCK_SIZE = 20;
const viewWidth = BLOCK_SIZE * COLS + PREVIEW_BLOCK_SIZE * 4 + 100, viewHeight = 500
const MainScene = Hilo.Class.create({
Extends: Scene,
currentBlock: null,
nextBlock: null,
isStart: false,
overGame:false,
totalStore:0,
level:1,
maxLevel:12,
init() {
this.grid = new Hilo.Graphics().addTo(this.root);
// preview
this.previewGrid = new Hilo.Graphics().addTo(this.root);
this.previewGrid.x = BLOCK_SIZE * COLS + 50
this.previewGrid.y = 50
this.blocks ={
size:BLOCK_SIZE,
rows:ROW,
cols:COLS,
offsetX:MARGIN[3],
offsetY:MARGIN[0],
}
this.blocks.map=this.createBlocks(this.blocks)
this.previewBlocks = {
size:PREVIEW_BLOCK_SIZE,
rows:4,
cols:4,
offsetX:this.previewGrid.x+MARGIN[3],
offsetY:this.previewGrid.y+MARGIN[0],
}
this.previewBlocks.map=this.createBlocks(this.previewBlocks)
new Hilo.DOMElement({
y:this.previewGrid.y+150,
x:this.previewGrid.x,
width:'auto',
element:Hilo.createElement('div',{
innerHTML:"记分板"
})
}).addTo(this.stage)
this.store=new Hilo.DOMElement({
y:this.previewGrid.y+150+30,
x:this.previewGrid.x,
width:'auto',
element:Hilo.createElement('div',{
style:{
color:'red'
},
innerHTML:""+this.totalStore
})
}).addTo(this.stage)
new Hilo.DOMElement({
y:this.previewGrid.y+250,
x:this.previewGrid.x,
width:'auto',
element:Hilo.createElement('div',{
innerHTML:"等级"
})
}).addTo(this.stage)
this.levelDom=new Hilo.DOMElement({
y:this.previewGrid.y+250+30,
x:this.previewGrid.x,
width:'auto',
element:Hilo.createElement('div',{
style:{
color:'red'
},
innerHTML:this.level
})
}).addTo(this.stage)
// this.fillMainBlock(0, 0, 'red')
// this.fillPreviewBlock(0, 0, 'red')
// 增加键盘事件
keyboardJS.bind('left', (e) => {
e.preventDefault()
this.moveBlock('left')
})
keyboardJS.bind('right', (e) => {
e.preventDefault()
this.moveBlock('right')
})
keyboardJS.bind('up', (e) => {
e.preventDefault()
this.moveBlock('up')
})
keyboardJS.bind('down', (e) => {
e.preventDefault()
this.moveBlock('down')
})
this.drawGrid(this.grid, BLOCK_SIZE, ROW, COLS)
this.drawGrid(this.previewGrid, PREVIEW_BLOCK_SIZE, 4, 4)
},
startGame() {
if (!this.isStart||this.overGame) {
this.isStart = true;
this.overGame=false
this.createNextBlock()
}
},
canMove(r, c, data) {
let canMove=true;
this.visitValidGrid(data, (valid, r2, c2) => {
let block=this.getMapData(r+r2,c+c2);
if(valid&&!block||block&&valid&&block.state==2){
canMove=false
return false;
}
})
return canMove
},
moveBlock(action) {
if(this.overGame){
return
}
if (!this.isStart) {
this.startGame()
return
}
const currentBlock = this.currentBlock
let r = currentBlock.r;
let c = currentBlock.c;
let data = currentBlock.data;
let directionIndex=currentBlock.directionIndex
switch (action) {
case "left":
c--;
break;
case "right":
c++;
break;
case "down":
r++;
break;
case "up":
let blockData=blocks[currentBlock.index].data
directionIndex=(currentBlock.directionIndex+1)%blockData.length
data=blockData[directionIndex]
break;
}
if (this.canMove(r, c, data)) {
currentBlock.data=data;
currentBlock.directionIndex=directionIndex
currentBlock.r = r;
currentBlock.c = c;
this.drawBlocks();
}else if(action==='down'){
if(currentBlock.r<0){
//over;
this.overGame=true;
this.setBlock(currentBlock,2);
this.drawBlocks()
return;
}
this.setBlock(currentBlock,2);
// 消除行
this.clearRows();
this.createNextBlock()
}
},
clearRows(){
let clearRowData=[],rows=this.blocks.rows,cols=this.blocks.cols,map=this.blocks.map
let clearRows=[]
for(let i=0;i<rows;i++){
let rows=[]
for(let j=0;j<cols;j++){
let data=map[i][j];
if(data.state==2){
rows.push(data)
}
}
if(rows.length===cols){
clearRowData=rows.concat(clearRowData)
clearRows.push(i)
}
}
if(!clearRowData.length){
return;
}
let complete=0
Hilo.Tween.fromTo(clearRowData.map(d=>({graph:d.graph,data:d})),{
scaleX:1,
scaleY:1,
alpha:1,
},{
scaleX:0,
scaleY:0,
alpha:0
},{
duration:200,
stagger:50,
onUpdate:function(p,self){
self.target.graph.scaleX=self.target.scaleX;
self.target.graph.scaleY=self.target.scaleY;
self.target.graph.alpha=self.target.alpha;
},
onComplete:(self)=>{
self.target.graph.clear()
self.target.graph.scaleX=1;
self.target.graph.scaleY=1;
self.target.graph.alpha=1;
self.target.data.color=''
self.target.data.state=0;
this.totalStore+=Math.pow(clearRows.length,2)*100;
this.store.drawable.domElement.innerHTML=this.totalStore
this.levelDom.drawable.domElement.innerHTML=Math.ceil(this.totalStore/10000)
complete++;
if(complete>=clearRowData.length){
clearRows.forEach(row=>{
map[row].forEach(d=>{
d.graph.removeFromParent()
})
map[row]=null
})
this.blocks.map=map.filter(Boolean).map(row=>{
row.forEach(d=>{
d.dirty=true
})
return row;
})
this.blocks.map=clearRows.map(()=>{
return Array.from({length:cols},(v2,colIndex)=>{
return this.createBlockData()
})
}).concat(this.blocks.map);
this.drawBlocks()
}
}
})
},
generateRandomBlock() {
const colorIndex = random(0, blockColors.length)
const index = random(0, blocks.length);
const blockTiledData = blocks[index].data
const directionIndex = random(0, blockTiledData.length);
const tiledData = blockTiledData[directionIndex]
const color = blockColors[colorIndex];
// 计算偏移,如果行是空,就向上移一行
let r = -1, rowOffset = 0, columnOffset = 0
let rows = new Array(4).fill(0)
let columns = new Array(4).fill(0)
while (++r < 4) {
let rData = (tiledData >> (3 - r << 2)) & 0xf;
if (rData) {
rows[r] += 1;
}
// 计算列偏移
let c = -1
while (++c < 4) {
let cData = (rData >> (3 - c)) & 0x1;
if (cData) {
columns[c] += 1;
}
}
}
rowOffset = rows.findIndex(d => d > 0)
rowOffset = rowOffset == -1 ? 0 : rowOffset;
let columnWidth = columns.filter(d => d > 0).length
columnOffset = columns.findIndex(d => d > 0)
columnOffset = columnOffset == -1 ? 0 : columnOffset;
const blockData = {
index: index,
directionIndex: directionIndex,
r: -rowOffset,
c: Math.floor((COLS - columnWidth) / 2) - columnOffset,
oy: rowOffset,
ox: columnOffset,
data: tiledData,
color: color
}
return blockData
},
createNextBlock() {
const current = this.nextBlock;
this.nextBlock = this.generateRandomBlock();
this.drawMap(this.previewBlocks)
if (!current) {
this.createNextBlock();
} else {
this.currentBlock = current;
this.drawBlocks();
}
},
createBlocks(blockInfo) {
return Array.from({
length: blockInfo.rows
}, (v, rowIndex) => {
return Array.from({length:blockInfo.cols},(v2,colIndex)=>{
return this.createBlockData()
})
})
},
createBlockData() {
return {
graph: null,
state: 0,
color:'',
dirty:false,
}
},
drawGrid(graph, size, row, col) {
const width = col * size, height = row * size
graph.beginPath()
graph.lineStyle('#000', LINE_WIDTH);
for (let r = 0; r <= row; r++) {
let x = MARGIN[3]
let y = MARGIN[0] + r * size
graph.moveTo(x, y)
graph.lineTo(x + width, y)
}
for (let c = 0; c <= col; c++) {
let x = MARGIN[3] + c * size
let y = MARGIN[0]
graph.moveTo(x, y)
graph.lineTo(x, y + height)
}
graph.endFill()
},
getMapData(row,col){
let {rows,cols,map}=this.blocks
if(row<0||row>=rows||col<0||col>=cols){
return null
}
return map[row][col]
},
setMapData(row,col,data){
let mapData=this.getMapData(row,col);
if(mapData){
Object.assign(mapData,data)
}
},
setPreviewBlock(block){
let map=this.previewBlocks.map;
this.visitValidGrid(block.data,(valid,r,c)=>{
if(valid){
map[r][c].color=block.color;
map[r][c].dirty=true
map[r][c].state=1;
}
})
},
setBlock(block,state=0){
this.visitValidGrid(block.data,(valid,r,c)=>{
if(valid){
this.setMapData(block.r+r,block.c+c,{
color:block.color,
dirty:true,
state:state
})
}
})
},
drawBlocks() {
if(this.nextBlock){
this.setPreviewBlock(this.nextBlock)
}
if(this.currentBlock){
this.setBlock(this.currentBlock,1)
}
this.drawMap(this.previewBlocks)
this.drawMap(this.blocks)
},
fillMap(row,col,size,offsetX,offsetY,mapItem){
if(!mapItem.dirty&&!this.overGame){
return
}
if(this.overGame&&mapItem.state===0){
return
}
let graph=mapItem.graph;
if(!graph&&mapItem.state!==0){
graph=mapItem.graph=new Hilo.Graphics().addTo(this.root)
}
graph&&graph.clear()
if(mapItem.state>0){
graph.beginPath()
graph.beginFill(this.overGame?'#bbbbbb':mapItem.color)
graph.drawRect(offsetX+col*size+LINE_WIDTH/2,offsetY+row*size+LINE_WIDTH/2,size-LINE_WIDTH,size-LINE_WIDTH)
graph.endFill()
}
if(mapItem.state==1){
mapItem.dirty=true;
mapItem.state=0;
}
},
drawMap(blockInfo){
let map=blockInfo.map;
for(let i=0;i<map.length;i++){
let rows=map[i]
for(let j=0;j<rows.length;j++){
let item=rows[j]
this.fillMap(i,j,blockInfo.size,blockInfo.offsetX,blockInfo.offsetY,item)
}
}
},
visitValidGrid(data, callback) {
for (let r = 0; r < 4; r++) {
const rData = (data >> ((3 - r) << 2)) & 0xf
for (let c = 0; c < 4; c++) {
const cData = (rData >> (3 - c)) & 0x1;
if(callback(cData === 1, r, c)===false){
return
}
}
}
},
time: 0,
update(delta) {
if (!this.isStart||this.overGame) {
return
}
this.time += delta
const speed=Math.max(0.05,1-this.level/this.maxLevel)
if (this.time > 1000*speed) {
this.time = 0
this.moveBlock('down')
}
}
})
let game = new Game({
enableDOMEvent: true,
container: left,
width: viewWidth,
height: viewHeight,
background: '#efefef',
dpr: window.devicePixelRatio,
scenes: [MainScene]
})
}
}
})