框架地址:https://github.com/maryrosecook/coquettegit
canvas2D上下文坐标github
开始于左上角,原点坐标(0,0);x越大表示越靠右,y越大表示越靠下canvas
fillRect()绘制矩形app
接受四个参数,分别是:矩形的x坐标,矩形的y坐标,矩形的宽,矩形的高。框架
c如今表明新建的coquette实例,是一个全局变量,下文会用到dom
var game = function () { this.c = new Coquette(this, "canvas", 500, 150, "#000"); }; window.addEventListener('load',function (ev) { new game(); });
Coquette的第三和第四个参数分别指定游戏区的宽和高,第五个参数指定背景颜色。ide
a.定义实体(建立一个定义实体的构造函数)函数
这个构造函数能够有如下两个参数学习
配置对象,能够在c.entities.create()的第二个参数中指定关于实体的配置对象。相似于:动画
var Person = function(game, settings) { this.c = game.c };
属性
实体有如下几个属性:
指定了center和size后,能够在draw方法中调用this.size/center来得到指定的size/center;指定了angle后,绘制出的实体会旋转指定的角度
范例代码:
this.draw = function(ctx) { ctx.fillStyle = settings.color; ctx.fillRect(this.center.x - this.size.x / 2, this.center.y - this.size.y / 2, this.size.x, this.size.y); };
方法
draw:在每一个最小时间间隔(tick)调用一次,能够在这个方法中指定如何绘制实体,好比指定填充颜色、绘制矩形(如上面的代码) update(timeSinceLastTick):更新一些实体的属性,以便在绘制的时候根据改变的属性从新绘制实体,产生动画的效果,参数是上一次tick距离如今过了多少时间。
b. 建立实体
调用c.entities.create()
接受以下参数:
返回建立的实体(能够将这个实体赋值给一个变量之后使用)
c. 摧毁实体
调用c.entities.destroy(实体)
与输入相关的属性和方法定义在c.inputter上
属性
LEFT_ARROW:左箭头 UP_ARROW:上箭头 DOWN_ARROW:下箭头 RIGHT_ARROW:右箭头 RIGHT_MOUSE:右鼠标 LEFT_MOUSE:左鼠标
方法
isDown(inputter):检测该按键是否处于按下的状态,只要一直按着,该方法返回true
isPress(inputter):在按键按下的紧随一个tick中返回true,以后返回false,直到松开后再次按下才再次返回一次true。
bindMouseMove(function(position){}):每次鼠标移动以后都会调用传给这个方法的函数:在函数的position参数中含有当前鼠标的位置(基于canvas2D上下文坐标)。
getMousePostion():获得当前鼠标的位置,返回{x:,y:}这样的对象
在实体相撞的时候触发
为了让实体支持碰撞,须要指定如下属性
例子:
var Player = function() { this.center = { x: 10, y: 20 }; this.size = { x: 50, y: 50 }; this.boundingBox = c.collider.CIRCLE; this.angle = 0; }; Player.prototype = { collision: function(other) { console.log("Ow,", other, "hit me."); } };
源码地址https://github.com/maryrosecook/coquette/blob/master/demos/spinning-shapes/game.js
CollisionCounter
一个构造函数,利用new建立出一个对象,拥有一个属性和两个方法。
CollisionCounter代码
var CollisionCounter = function(entity) { this.colliders = []; this.update = function() { this.colliders = this.colliders .filter(function(c) { return entity.c.collider.isColliding(entity, c); }); }; this.collision = function(other) { if (this.colliders.indexOf(other) === -1) { this.colliders.push(other); } }; };
randomDirection
给unitVector传入0~0.5的随机坐标
var randomDirection = function() { return Coquette.Collider.Maths.unitVector({ x:Math.random() - .5, y:Math.random() - .5 }); };
unitVector
unitVector: function(vector) { return { x: vector.x / Maths.magnitude(vector), y: vector.y / Maths.magnitude(vector) }; },
magnitude
magnitude: function(vector) { return Math.sqrt(vector.x * vector.x + vector.y * vector.y); },
最终结果,返回
{x:ramX/sqrtramX*ramX+ramY*ramY),y:ramY/sqrt(ramX*ramX+ramY*ramY)}//利用坐标表示从0到360的任意角度
其中-0.5<x<0.5;-0.5<y<0.5
movingonscreenVec
代码:
var movingOnscreenVec = function(dirFromCenter) { return { x: -dirFromCenter.x * 3 * Math.random(), y: -dirFromCenter.y * 3 * Math.random() } };
假设传入对象为{x:x,y:y},返回:
{x:-3x*ram(0~1),y:-3y*ram(0~1)}
offscreenPosition
代码:
var offscreenPosition = function(dirFromCenter, viewSize, viewCenter) { return { x: viewCenter.x + dirFromCenter.x * viewSize.x, y: viewCenter.y + dirFromCenter.y * viewSize.y }; };
其中viewSize和viewCenter分别是canvas的长宽和canvas中心点的坐标。这个函数的做用是返回新生成的实体的中点坐标,值得注意的是,中点坐标必定在可视区以外,可是若是实体的长或者高太大,实体会有一部分一开始就在canvas可视区以内。
isOutOfView
isOutOfView
var isOutOfView = function(obj, viewSize, viewCenter) { return Coquette.Collider.Maths.distance(obj.center, viewCenter) > Math.max(viewSize.x, viewSize.y); };
Maths.distance
distance: function(point1, point2) { var x = point1.x - point2.x; var y = point1.y - point2.y; return Math.sqrt((x * x) + (y * y)); },
判断某个实体是否彻底在可视区以外,当某个实体的中点距离canvas中心的距离大于canvas可视区的最大边长的时候,返回true。实体边长过长可能会出现还没实体彻底脱离可视区就被摧毁的状况。
var SpinningShapesGame = function() { var autoFocus = false; this.c = new Coquette(this, "spinning-shapes-canvas", 500, 500 / GOLDEN_RATIO, "white", autoFocus); this.dragger = new Dragger(this.c); // controls dragging of shapes with mouse }; SpinningShapesGame.prototype = { update: function() { this.dragger.update(); var viewSize = this.c.renderer.getViewSize(); var viewCenter = this.c.renderer.getViewCenter(); if (this.c.entities.all().length < 15) { // not enough shapes var dirFromCenter = randomDirection(); var Shape = Math.random() > 0.5 ? Rectangle : Circle; this.c.entities.create(Shape, { // make one center: offscreenPosition(dirFromCenter, viewSize, viewCenter), vec: movingOnscreenVec(dirFromCenter) }); } // destroy entities that are off screen var entities = this.c.entities.all(); for (var i = 0; i < entities.length; i++) { if (isOutOfView(entities[i], viewSize, viewCenter)) { this.c.entities.destroy(entities[i]); } } } };
Dragger的原型里有一个update方法,这个方法在每一个tick都更新,它会检测鼠标左键是否按下,若是按下,调用isDragging检测是否处于拖拽状态,若是为否,检测鼠标是否处于某个实体的范围内,若是是的,调用startDrag方法给Dragger实例设置一个currentDrag属性,该属性是一个对象,含有拖拽目标实体和鼠标偏移坐标。
Dragger函数原型
Dragger.prototype = { update: function() { if (this.c.inputter.isDown(this.c.inputter.LEFT_MOUSE)) { if (!this._isDragging()) { var mousePosition = this.c.inputter.getMousePosition(); var target = this._getTarget(this.c.entities.all(), mousePosition); if (target !== undefined) { this._startDrag(target, mousePosition); } } } else { this._stopDrag(); } }, _isDragging: function() { return this._currentDrag !== undefined; }, _getTarget: function(targets, e) { for (var i = 0; i < targets.length; i++) { if (Coquette.Collider.Maths.pointInsideObj(e, targets[i])) { return targets[i]; } } }, _startDrag: function(target, e) { this._currentDrag = { target: target, centerOffset: { x: target.center.x - e.x, y: target.center.y - e.y } }; if (target.startDrag !== undefined) { target.startDrag(); } }, _stopDrag: function() { if (this._isDragging()) { if (this._currentDrag.target.stopDrag !== undefined) { this._currentDrag.target.stopDrag(); } this._currentDrag = undefined; } } };
拖拽状态是调用startDrag方法肯定的,实际上就是给Dragger实例添加一个currentDrag属性。取消拖拽状态是调用stopDrag方法肯定的,实际上就是将currentDrag属性设置为undefined。
Dragger函数
var Dragger = function(c) { this.c = c; this._currentDrag; var self = this; c.inputter.bindMouseMove(function(e) { if (c.inputter.isDown(c.inputter.LEFT_MOUSE)) { if (self._isDragging()) { self._currentDrag.target.center = { x: e.x + self._currentDrag.centerOffset.x, y: e.y + self._currentDrag.centerOffset.y }; } } }); };
bindMouseMove是coquette的原生方法,在每次鼠标移动的时候就会调用传入的匿名函数。匿名函数的功能是:在鼠标按下,而且处于拖拽状态的状况下,更新拖拽实体的中心坐标。