6??? 用户交互――移动物体
????? 游戏的核心在于交互,很多时候需要用户动手来操作游戏对象,很基本的一个操作就是移动物体。接下来我们会介绍如何拖动物体,在画布上扔物体等。
6.1??? 选择与释放对象
??????? 使用鼠标对物体的拖拽操作主要有三个步骤――鼠标进入物体范围并按下,鼠标移动及鼠标释放。这涉及到三个鼠标事件:mousedown,mousemove,mouseup。我们按此思路,仍旧以前面介绍的小球系统来实现。
??????? 首先需要检测到鼠标是否进入到小球中,为了简化处理,我们把小球看成一个与其外接矩形等效的图形。先得到小球在画布中所占据的位置。如下代码:
ball.prototype.getBounds=function(){
return { x: ball.x-ball.radius, y: ball.y-ball.radius,
width: 2*ball.radius, height: 2*ball.radius };
}
?
??? 如上,得到了小球外接矩形的左上端点及长宽。接下来,我们就需要来查看鼠标是否进入小球,抽象出来就是点是否在当前的矩形中,在其我们使用集合思想,取其对立事件就很容易处理。如下代码:
utils.containsPoint = function (rect, x, y) {
return !(x < rect.x || x > rect.x + rect.width ||
y < rect.y || y > rect.y + rect.height);
};
?
??? 很明白地看到,我们先取得点不在小球的四种情况,然后取其对立事件即为点在小球内。于是很容易就实现:
window.onload=function(){
var canvas=document.getElementById("canvas");
var context=canvas.getContext("2d");
var mouse=utils.captureMouse(canvas);
var ball=new Ball();
ball.x=canvas.width/2;
ball.y=canvas.height/2;
ball.draw(context);
//鼠标按键按下事件
canvas.addEventListener("mousedown",function(event){
if(utils.containsPoint(ball.getBounds(),mouse.x,mouse.y)){
console.log("===[mousedown]===in ball");
}else{
console.log("===[mousedown]===out ball");
}
},false);
//鼠标按键释放事件
canvas.addEventListener("mouseup",function(event){
if(utils.containsPoint(ball.getBounds(),mouse.x,mouse.y)){
console.log("***[mouseup]===in ball");
}else{
console.log("***[mouseup]===out ball");
}
},false);
//鼠标移动事件
canvas.addEventListener("mousemove",function(event){
if(utils.containsPoint(ball.getBounds(),mouse.x,mouse.y)){
console.log("###[mousemove]###in ball");
}else{
console.log("###[mousemove]###out ball");
}
})
};
?
?
6.2??? 拖动对象
???????? 有了上面的实践基础,实现拖动非常容易,只需要在鼠标移动时让小球的位置与鼠标位置相同即可。如下代码:
window.onload=function(){
var canvas=document.getElementById("canvas");
var context=canvas.getContext("2d");
var mouse=utils.captureMouse(canvas);
var ball=new Ball(40);
ball.x=canvas.width/2;
ball.y=canvas.height/2;
ball.draw(context);
//小球跳动偏差处理
var offset_fix_x=0;
var offset_fix_=0;
//鼠标按下时检测是否位于小球内部,如果位于,则注册移动事件处理函数
canvas.addEventListener("mousedown",function(event){
if(utils.containsPoint(ball.getBounds(),mouse.x,mouse.y)){
offset_fix_x=mouse.x-ball.x;
offset_fix_y=mouse.y-ball.y;
canvas.addEventListener("mousemove",onMouseMove,false);
canvas.addEventListener("mouseup",onMouseUP,false);
}
},false);
//鼠标按键释放时的操作
function onMouseUP(){
canvas.removeEventListener("mouseup",onMouseUP,false);
canvas.removeEventListener("mousemove",onMouseMove,false);
}
//鼠标移动时小球跟随鼠标
function onMouseMove(event){
ball.x=mouse.x-offset_fix_x;
ball.y=mouse.y-offset_fix_y;
}
(function drawFrame(){
window.requestAnimFrame(drawFrame,canvas);
context.clearRect(0,0,canvas.width,canvas.height);
ball.draw(context);
})();
};
?
??? 很容易就实现了小球移动的功能。注意,offset_x与offset_y是为了用来处理在小球内部我们快速点击小球边缘产生的小球跳动问题。通过记录鼠标按下时鼠标与小球中心的偏差距离,在鼠标移动时固定处理这个偏差,就能避免跳动。
6.3??? 拖动移动中的对象
????? 如果需要对正在移动中的对象进行拖拽动作,原理和前面介绍的静止的对象拖拽一样,但需要注意,我们必须处理小球在鼠标点击后的状态,将此时小球的速度置0,否则我们鼠标在点住小球后,小球会继续沿其之前的速度运动,这样很难进行拖拽操作。如下代码:
window.onload=function(){
var canvas=document.getElementById("canvas");
var context=canvas.getContext("2d");
var mouse=utils.captureMouse(canvas);
var left=0,top=0,right=canvas.width,bottom=canvas.height,isMouseDown=false;
var ball=new Ball(40);
//跳动偏差处理
var fix_offset_jump_x=fix_offset_jump_y=0;
//小球移动状态初始化
ball.x=canvas.width/2;
ball.y=canvas.height/2;
ball.vx=Math.random()*10-5;
ball.vy=-10;
bounce=-0.7;
gravity=0.2;
//鼠标按下时检测是否位于小球内部,如果位于,则注册移动事件处理函数
canvas.addEventListener("mousedown",function(){
if(utils.containsPoint(ball.getBounds(),mouse.x,mouse.y)){
isMouseDown=true;
//如果鼠标在小球内部,则使得小球静止
?ball.vx=0;
ball.vy=0;
fix_offset_jump_x=mouse.x-ball.x;
fix_offset_jump_y=mouse.y-ball.y;
canvas.addEventListener("mousemove",onMouseMove,false);
canvas.addEventListener("mouseup",onMouseUP,false);
}
},false);
function onMouseUP(){
isMouseDown=false;
canvas.removeEventListener("mousemove",onMouseMove,false);
canvas.removeEventListener("mouseup",onMouseUP,false);
}
function onMouseMove(){
ball.x=mouse.x-fix_offset_jump_x;
ball.y=mouse.y-fix_offset_jump_y;
}
//边缘检测,碰撞处理
function checkBoundary(){
ball.vy+=gravity;
ball.x+=ball.vx;
ball.y+=ball.vy;
if(ball.y-ball.radius<top){
ball.y=ball.radius+top;
ball.vy*=bounce;
console.log(ball.vy);
}else if(ball.y+ball.radius>bottom){
ball.y=bottom-ball.radius;
ball.vy*=bounce;
}
if(ball.x-ball.radius<left){
ball.x=ball.radius+left;
ball.vx*=bounce;
}else if(ball.x+ball.radius>right){
ball.x=right-ball.radius;
ball.vx*=bounce;
}
}
(function drawFrame(){
window.requestAnimFrame(drawFrame,canvas);
context.clearRect(0,0,canvas.width,canvas.height);
if(!isMouseDown){
checkBoundary();
}
ball.draw(context);
})();
};
?
6.4??? 投掷对象
???????? 对于对象的投掷动作看似复杂,其实结合之前的拖拽动作,它的实现就非常简单。假设我们把一个静止的小球在一帧中从原始位置水平移动了10像素后松开鼠标,即实现了扔的动作,那么我们就认为此时小球被以10像素/ 帧的速度水平投掷出来。即
ball.vx=ball.x-lastX; ball.vy=ball.y-lastY;
?
??? 于是,我们对移动中的小球进行投掷动作,代码如下:
window.onload=function(){
var canvas=document.getElementById("canvas");
var context=canvas.getContext("2d");
var mouse=utils.captureMouse(canvas);
//定义系统环境变量
?var top=0,left=0,right=canvas.width,left=0,bottom=canvas.height;
var fix_mouse_drag_offset_x=0;
var fix_mouse_drag_offset_y=0;
var bounce=-0.7;
var gravity=0.2;
var isMouseDown=false;
var oldX=oldY=0;
//初始化小球
var ball=new Ball(40);
ball.x=canvas.width/2;
ball.y=canvas.height/2;
ball.vx=Math.random()*10-5;
ball.vy=-10;
//鼠标按下时检测鼠标是否位于小球内部
canvas.addEventListener("mousedown",function(){
if(utils.containsPoint(ball.getBounds(),mouse.x,mouse.y)){
isMouseDown=true;
//小球跳动处理
fix_mouse_drag_offset_x=mouse.x-ball.x;
fix_mouse_drag_offset_y=mouse.y-ball.y;
//将小球置于静止状态
?oldX=ball.x;
oldY=ball.y;
mouse.vx=mouse.vy=0;
canvas.addEventListener("mousemove",onMouseMove,false);
canvas.addEventListener("mouseup",onMouseUp,false);
}
},false);
function onMouseMove(){
ball.x=mouse.x-fix_mouse_drag_offset_x;
ball.y=mouse.y-fix_mouse_drag_offset_x;
}
function onMouseUp(){
isMouseDown=false;
canvas.removeEventListener("mousemove",onMouseMove,false);
canvas.removeEventListener("mouseup",onMouseUp,false);
}
//计算小球被投掷后的速度
function trackVelocity(){
ball.vx=ball.x-oldX;
ball.vy=ball.y-oldY;
oldX=ball.x;
oldY=ball.y;
}
//边缘检测,碰撞出理
function checkBoundary(){
ball.vy+=gravity;
ball.x+=ball.vx;
ball.y+=ball.vy;
if(ball.x-ball.radius<left){
ball.x=left+ball.radius;
ball.vx*=bounce;
}else if(ball.x+ball.radius>right){
ball.x=right-ball.radius;
ball.vx*=bounce;
}
if(ball.y-ball.radius<top){
ball.y=top+ball.radius;
ball.vy*=bounce;
}else if(ball.y+ball.radius>bottom){
ball.y=bottom-ball.radius;
ball.vy*=bounce;
}
}
(function drawFrame(){
window.requestAnimFrame(drawFrame,canvas);
context.clearRect(0,0,canvas.width,canvas.height);
//鼠标按下时,进行投掷处理,否则进行边缘检测及碰撞处理
?if(!isMouseDown){
checkBoundary();
}else{
trackVelocity();
}
ball.draw(context);
})();
};
?
?