8.4.1.6_图像与精灵的碰撞检测
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>图像与精灵的碰撞检测</title>
<style>
body{
background: #fff;
}
#canvas{
background: #eee;
}
</style>
</head>
<body>
<canvas id="canvas" width="800" height="600"></canvas>
</body>
<script>
var Sprite = function(name,painter,behaviors){
if(name !== undefined){ this.name = name; }
if(painter !== undefined){ this.painter = painter; }
this.top = 0;
this.left = 0;
this.width = 10;
this.height = 10;
this.velocityX = 0;
this.velocityY = 0;
this.visible = true;
this.animating = false;
this.behaviors = behaviors || [];
}
Sprite.prototype = {
paint:function(context){
if(this.painter !== undefined && this.visible){
this.painter.paint(this,context);
}
},
update:function(context,time){
for(var i=0;i<this.behaviors.length;i++){
this.behaviors[i].execute(this,context,time);
}
}
}
</script>
<script>
var ImagePainter = function (imageUrl){
var self = this;
this.image = new Image();
this.loaded = false;
this.image.src = imageUrl;
this.image.addEventListener('load',function(){
self.loaded = true;
},false);
}
ImagePainter.prototype ={
paint:function(sprite,context){
context.drawImage(this.image,sprite.left,sprite.top,sprite.width,sprite.height);
}
}
</script>
<script>
var Vector = function(x,y){
this.x = x;
this.y = y;
};
Vector.prototype = {
getMagnitude:function(){
return Math.sqrt(Math.pow(this.x,2)+Math.pow(this.y,2));
},
add:function(anotherVector){
var v = new Vector();
v.x = this.x + anotherVector.x;
v.y = this.y + anotherVector.y;
return v;
},
subtract:function(anotherVector){
var v = new Vector();
v.x = this.x - anotherVector.x;
v.y = this.y - anotherVector.y;
return v;
},
dotProduct:function(anotherVector){
return this.x *anotherVector.x+ this.y*anotherVector.y;
},
edge:function(anotherVector){
return this.subtract(anotherVector);
},
perpendicular:function(){
var v = new Vector();
v.x = this.y;
v.y = 0-this.x;
return v;
},
normalize:function(){
var v = new Vector(0,0);
var m = this.getMagnitude();
if(m!=0){
v.x = this.x/m;
v.y = this.y/m;
}
return v;
},
perpendicularNormal:function(){
var p = this.perpendicular();
return p.normalize();
}
}
</script>
<script>
var Projection = function(min,max){
this.min = min;
this.max = max;
};
Projection.prototype = {
overlaps:function(projection){
return this.max > projection.min && projection.max >this.min;
}
}
</script>
<script>
var Point = function(x,y){
this.x = x;
this.y = y;
}
</script>
<script>
var Shape = function(){
this.x = undefined;
this.y = undefined;
this.strokeStyle = 'rgba(255,253,208,0.9)';
this.fillStyle = 'rgba(147,197,114,0.8)';
};
Shape.prototype = {
collidesWith:function(shape){
var axes = this.getAxes().concat(shape.getAxes());
return !this.separationOnAxes(axes,shape);
},
separationOnAxes:function(axes,shape){
var axis;
var projection1;
var projection2;
for(var i =0;i<axes.length;++i){
axis = axes[i];
projection1 = shape.project(axis);
projection2 = this.project(axis);
if(!projection1.overlaps(projection2)){
return true;
}
};
return false;
},
project:function(axis){
throw 'project(axis) 还没有实现呢';
},
getAxes:function(){
throw 'getAxes() 还没有实现呢';
},
move:function(dx,dy){
throw 'move(dx,dy) 还没有实现呢';
},
creatPath:function(context){
throw 'creatPath(context) 还没有实现呢';
},
fill:function(context){
context.save();
context.fillStyle = this.strokeStyle;
this.creatPath(context);
context.fill();
context.restore();
},
stroke:function(){
context.save();
context.strokeStyle = this.strokeStyle;
this.creatPath(context);
context.stroke();
context.restore();
},
isPointInPath:function(context,x,y){
this.creatPath(context);
return context.isPointInPath(x,y);
}
};
</script>
<script>
var Polygon = function(){
this.points = [];
this.strokeStyle = 'blue';
this.fillStyle = 'white';
};
Polygon.prototype = new Shape();
Polygon.prototype.collidesWith = function(shape){
var axes = shape.getAxes();
if(axes === undefined ){
return polygonCollidesWithCircle(this,shape);
}else{
return !this.separationOnAxes(axes.concat(this.getAxes()),shape);
}
}
Polygon.prototype.getAxes = function(){
var v1 = new Vector();
var v2 = new Vector();
var axes = [];
for(var i =0; i<this.points.length-1;i++){
v1.x = this.points[i].x;
v1.y = this.points[i].y;
v2.x = this.points[i+1].x;
v2.y = this.points[i+1].y;
axes.push(v1.edge(v2).perpendicularNormal());
};
v1.x = this.points[this.points.length-1].x;
v1.y = this.points[this.points.length-1].y;
v2.x = this.points[0].x;
v2.y = this.points[0].y;
axes.push(v1.edge(v2).perpendicularNormal());
return axes;
};
Polygon.prototype.project = function(axis){
var scalars = [];
var v = new Vector();
this.points.forEach(function(point){
v.x = point.x;
v.y = point.y;
scalars.push(v.dotProduct(axis));
});
return new Projection(Math.min.apply(Math,scalars),Math.max.apply(Math,scalars));
};
Polygon.prototype.addPoint = function(x,y){
this.points.push(new Point(x,y));
};
Polygon.prototype.creatPath = function(context){
if(this.points.length === 0){
return
};
context.beginPath();
context.moveTo(this.points[0].x,this.points[0].y);
for(var i =0 ;i<this.points.length;i++){
context.lineTo(this.points[i].x,this.points[i].y)
}
context.closePath();
};
Polygon.prototype.move = function(dx,dy){
for(var i =0,point; i<this.points.length;i++){
point = this.points[i];
point.x += dx;
point.y += dy;
}
};
</script>
<script>
var Circle = function(x,y,radius){
this.x = x;
this.y = y;
this.radius = radius;
this.strokeStyle = 'rgba(255,253,208,0.9)';
this.fillStyle = 'rgba(147,197,114,0.8)';
}
Circle.prototype = new Shape();
Circle.prototype.collidesWith = function(shape){
var point;
var length;
var min = 1000;
var v1;
var v2;
var edge;
var perpendicular;
var normal;
var axes = shape.getAxes();
var distance;
if(axes ==undefined){
distance = Math.sqrt(Math.pow(shape.x -this.x,2)+Math.pow(shape.y -this.y,2));
return distance < Math.abs(this.radius + shape.radius);
}else{
return polygonCollidesWithCircle(shape,this);
}
}
Circle.prototype.getAxes = function(){
return undefined;
};
Circle.prototype.project = function(axis){
var scalars =[];
var point = new Point(this.x,this.y);
var dotProduct = new Vector(point.x,point.y).dotProduct(axis);
scalars.push(dotProduct);
scalars.push(dotProduct + this.radius);
scalars.push(dotProduct - this.radius);
return new Projection(Math.min.apply(Math,scalars),Math.max.apply(Math,scalars));
};
Circle.prototype.move = function(dx,dy){
this.x += dx;
this.y += dy;
};
Circle.prototype.creatPath = function(context){
context.beginPath();
context.arc(this.x,this.y,this.radius,0, Math.PI*2,false);
}
</script>
<script>
function getPolygonPointClosestToCircle(polygon,circle){
var min = 1000;
var length;
var testPoint;
var closestPoint;
for(var i =0 ;i<polygon.points.length;i++){
testPoint = polygon.points[i];
length = Math.sqrt(Math.pow(testPoint.x -circle.x,2)+Math.pow(testPoint.y-circle.y,2));
if(length <min){
min = length;
closestPoint = testPoint;
}
};
return closestPoint;
};
function polygonCollidesWithCircle(polygon,circle){
var v1;
var v2;
var axes = polygon.getAxes();
var closestPoint = getPolygonPointClosestToCircle(polygon,circle);
v1 = new Vector(circle.x,circle.y);
v2 = new Vector(closestPoint.x,closestPoint.y);
axes.push(v1.subtract(v2).normalize());
return !polygon.separationOnAxes(axes,circle);
}
</script>
<script>
var ImageShape = function(imageSource,x,y,w,h){
var self = this;
this.image = new Image();
this.imageLoaded = false;
this.points = [ new Point(x,y)];
this.x = x;
this.y = y;
this.image.width = w;
this.image.height = h;
this.image.src = imageSource;
this.image.addEventListener('load',function(e){
self.setPolygonPoints();
self.imageLoaded = true;
},false);
};
ImageShape.prototype = new Polygon();
ImageShape.prototype.fill = function(context){
};
ImageShape.prototype.setPolygonPoints = function(){
this.points.push(new Point(this.x + this.image.width,this.y));
this.points.push(new Point(this.x+this.image.width,this.y+this.image.height));
this.points.push(new Point(this.x,this.y+this.image.height));
};
ImageShape.prototype.drawImage = function(context){
context.drawImage(this.image,this.points[0].x,this.points[0].y,this.image.width,this.image.height);
};
ImageShape.prototype.stroke = function(context){
var self = this;
if(this.imageLoaded){
context.drawImage(this.image,this.points[0].x,this.points[0].y,this.image.width,this.image.height);
}else{
this.image.addEventListener('load',function(e){
self.drawImage(context);
},false);
}
}
</script>
<script>
var SpriteShape = function(sprite,x,y,w,h){
this.sprite = sprite;
this.imageLoad = sprite.painter.loaded;
this.x = x;
this.y = y;
sprite.left = x;
sprite.top = y;
sprite.width = w;
sprite.height = h;
this.setPolygonPoints();
};
SpriteShape.prototype = new Polygon();
SpriteShape.prototype.move = function(dx,dy){
var point,x;
for(var i =0;i<this.points.length;i++){
point = this.points[i];
point.x += dx;
point.y += dy;
};
this.sprite.left = this.points[0].x;
this.sprite.top = this.points[0].y;
};
SpriteShape.prototype.fill = function(context){
};
SpriteShape.prototype.setPolygonPoints = function(){
this.points.push(new Point(this.x,this.y));
this.points.push(new Point(this.x+this.sprite.width,this.y));
this.points.push(new Point(this.x+this.sprite.width,this.y + this.sprite.height));
this.points.push(new Point(this.x,this.y+this.sprite.height));
};
SpriteShape.prototype.stroke = function(context){
var self = this;
if(this.imageLoad){
console.log('后续执行的绘制');
self.sprite.painter.paint(self.sprite,context);
}else{
console.log('加载完');
this.sprite.painter.image.addEventListener('load',function(){
self.sprite.painter.paint(self.sprite,context);
self.imageLoad = true;
},false);
}
}
</script>
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var shapes = [];
var ballSprite = new Sprite('ball',new ImagePainter('img/tenniBall.png'));
var polygonPoints = [
[new Point(250,150), new Point(250,250), new Point(350,250)],
[new Point(100,100),new Point(100,150), new Point(150,150),new Point(150,100)],
[new Point(400,100),new Point(380,150),new Point(500,150),new Point(520,100)]
];
var polygonStrokeStyles = ['blue','yellow','red'];
var polygonFillStyles = ['rgba(255,255,0,0.7)','rgba(100,140,230,0.6)','rgba(255,255,255,0.8)'];
var mousedown = {x:0,y:0};
var lastdrag = {x:0,y:0};
var shapeBeingDragged = undefined;
var circle1 = new Circle(150,75,20);
var circle2 = new Circle(350,25,30);
shapes.push(circle1);
shapes.push(circle2);
shapes.push(new ImageShape('img/gaofu.png',50,50,50,50));
shapes.push(new SpriteShape(ballSprite,300,100,50,50));
for(var i =0 ;i<polygonPoints.length;i++){
var polygon = new Polygon();
var points = polygonPoints[i];
polygon.strokeStyle = polygonStrokeStyles[i];
polygon.fillStyle = polygonFillStyles[i];
points.forEach(function(point){
polygon.addPoint(point.x,point.y);
});
shapes.push(polygon);
};
context.shadowColor = 'rgba(100,140,255,0.5)';
context.shadowBlur = 4;
context.shadowOffsetX = 2;
context.shadowOffsetY =2;
context.font = '38px arial';
drawShapes();
context.save();
context.fillStyle = 'cornflowerblue';
context.font = '24px arial';
context.fillText('拖拽图形相碰撞', 10,25);
context.restore();
function windowToCanvas(x,y){
var bbox = canvas.getBoundingClientRect();
return{
x:x - bbox.left*(canvas.width/bbox.width),
y:y - bbox.top*(canvas.height/bbox.height)
};
};
function drawShapes(){
shapes.forEach(function(shape){
shape.stroke(context);
shape.fill(context);
});
}
function detectCollosions(){
var textY = 30;
var numShapes = shapes.length;
var shape;
var i;
if(shapeBeingDragged){
for(var i = 0; i<numShapes; ++i){
shape = shapes[i];
if(shape !=shapeBeingDragged ){
if(shapeBeingDragged.collidesWith(shape)){
context.fillStyle = shape.fillStyle;
context.fillText('碰撞了',20,textY);
textY += 40;
}
}
}
}
}
canvas.onmousedown = function(e){
var location = windowToCanvas(e.clientX,e.clientY);
shapes.forEach(function(shape){
if(shape.isPointInPath(context,location.x,location.y)){
shapeBeingDragged = shape;
mousedown.x = location.x;
mousedown.y = location.y;
lastdrag.x = location.x;
lastdrag.y = location.y;
}
});
};
canvas.onmousemove = function(e){
var location;
var dragVector;
if(shapeBeingDragged !=undefined){
location = windowToCanvas(e.clientX,e.clientY);
dragVector = {x:location.x - lastdrag.x,
y:location.y - lastdrag.y};
shapeBeingDragged.move(dragVector.x,dragVector.y);
lastdrag.x = location.x;
lastdrag.y = location.y;
context.clearRect(0,0,canvas.width,canvas.height);
drawShapes();
detectCollosions();
}
};
canvas.onmouseup = function(e){
shapeBeingDragged = undefined;
}
</script>
</html>
![这里写图片描述](https://img-blog.csdn.net/20170629170750151?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvanVuX3N1bl9zaGluZQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![这里写图片描述](https://img-blog.csdn.net/20170629170803040?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvanVuX3N1bl9zaGluZQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)