Pixi.js学习 (七)碰撞检测与定时动画
目录
前言
为了提高作者的代码编辑水品,作者在使用博客的时候使用的集成工具为 HBuilderX。
下文所有截图使用此集成工具,读者随意。
此系列文章需要:HTML ,JavaScript 基础知识
操作系统:
版本 Windows 11 家庭中文版
版本 23H2
安装日期 2024/5/28
操作系统版本 22631.3593
体验 Windows Feature Experience Pack 1000.22700.1003.0
一、碰撞
概念: 碰撞,指的是窗口中两张图片是否有交集。如果有交集,则认为两张图片发生碰撞。
如下图所示:

2.1 、假想圆
因为图片形状各异,判断两张图片是否有交集,似乎不太好做,所以为了判断方便,我们把每张图片都看做一个圆,这个圆,就是我们通常所说的假想圆。
如下图:

上图中,飞机图片和子弹图片都有个红色的圆,这个圆就是假想圆。
2.2、碰撞距离
有了假想圆,在判断碰撞时就容易多了,我们只需要判断两个假想圆是否有交集就可以了。 如下图:

通过上图可以看出,当A、 B两张图片中心点距离小于A、 B两个假想圆的半径之和时 我们就认为两张图片发生了碰撞。
可问题是:两个假想圆的半径是固定的,但AB中心点的距离如何获得呢?
2.3、利用勾股定理计算距离
如果我们想要获得AB两个中心点距离,那么必须要用上勾股定理了。
勾股定理:在直角三角形中,两条直角边的平方和,等于斜边的平方
| 1 虽然现在c边的距离我们不知道,但a边和b边的距离我们是能求出来的 2 a 边的距离长度 = 子弹的y坐标 – 飞机的y坐标 3 b 边的距离长度 = 子弹的x坐标 – 飞机的x坐标 4 所以通过勾股定理,我们同样可以求出 c 边长度的平方值 |
计算公式如下
a2+b2==c2
| 1 如果两元素距离 小于等于 碰撞距离,则视为两元素发生碰撞 2 如果两元素距离的平方值 小于等于 碰撞距离的平方值,则视为两元素发生碰撞 |
如下图:

运行效果
如下图所示

示例代码
<script type="text/javascript">
var app=new PIXI.Application(500,700);
document.body.appendChild(app.view);
function add(img,x,y){
//创建图片元素
var a=new PIXI.Sprite.fromImage(img);
//将元素添加到页面中
app.stage.addChild(a);
//调整位置
a.x=x
a.y=y
//对当前元素直接开启监听
a.interactive=true;
return a;
}
var bg = add("img/bg.jpg",0,0);
var enemy=add("img/enemy_02.png",150,100);
enemy.anchor.set(0.5,0.5)
var bullet=add("img/bullet_1.png",150,350)
bullet.anchor.set(0.5,0.5)
//自定义帧频函数,子弹向上移动
function animate(){
//如果子弹与飞机接触则移除飞机元素
//计算子弹与飞机y轴距离长度
var yl = bullet.y - enemy.y
//计算子弹与飞机x轴距离长度
var xl = bullet.x - enemy.x
//pos :触碰距离
var pos = 52+25;
if( (yl*yl) + (xl*xl) < pos*pos ){
//从应用窗口中移除敌机元素
app.stage.removeChild( enemy )
}
//子弹向上移动
bullet.y -= 1;
}
//添加帧频函数
app.ticker.add( animate );
</script>
2.4、碰撞判断步骤总结
| 1、根据元素宽高特点划定假想圆 ,元素锚点为中心,作为假想圆的圆心位置 2、计算碰撞距离:两个元素假想圆半径之和,如果两元素间距小于或等于碰撞距离,则视为发生碰撞 3 3、计算两个元素x轴距离与y轴距离(距离既边长) 4、计算两个元素间距:根据勾股定理,得出x轴距离平方+y轴距离平方==间距平方 5、如果间距的平方<=碰撞距离的平方(间距<=碰撞距离, 由于得到的间距是平方值,所以碰撞距离 也要用平方值) |
例
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | A图片假想圆半径:A图片宽高大约为80像素,那么假想圆的半径约为40像素 B图片假想圆半径:B图片宽高大约为40像素,那么假想圆的半径约为20像素 通过以上得出,碰撞距离为:AB边的距离 < 60,这时我们就认为发生碰撞 计算x轴距(边长) :A图片x轴坐标150,B图片x轴坐标180,B.x-A.x 就是两个元素x轴距 计算y轴距(边长) :A图片y轴坐标50,B图片x轴坐标170,B.y-A.y 就是两个元素y轴距 通过以上得出,x轴距为30,y轴距为120(轴距即边长) 根据勾股定理求出斜边长度的平方,如果斜边长度小于碰撞距离的平方此时发生碰撞 30与120是两个直角边长度,两个直角边长度的平方和等于斜边长度的平方 计算方式: (30*30+120*120) < 60*60 两个元素发生碰撞 可以使用Math.sqrt( );开方 计算方式:Math.sqrt(30*30+120*120) < 60 |
2.5 图层编号
获取图层编号
| 1 app.stage.getChildIndex(classify3); |
设置图层编号
| 1 //将元素的图层设置为指定编号级别 2 app.stage.setChildIndex(元素 ,图层编号); |
注意:如果两元素图层一致,后添加的元素会置于制定编号位置,先添加的元素会自动提高一级。
二、定时动画
实现方式 帧频函数每秒执行60次,可以自定义计数变量,当帧频开始执行时计数变量+1 当计数变量的值为60时,函数执行了60次,时间过了一秒。
//定时器
var count = 1;
function animate(){
//判断每60次执行一次
if(count == 60){
var enemy1 = add("img/enemy_02.png",Math.random()*395,Math.random()*625);
count = 0;
}
//定时器增加时间
count++;
}
app.ticker.add(animate);
count的值变为60,帧频函数执行了60次,证明时间过去了一秒。
count的值变为60后,执行一次动画效果并将count的值重置,重新计数
三、实战
例题一:飞机增益道具
- 1. 创建一个名为 app 的应用,宽:500,高:768
- 2.利用碰撞判断,判断飞机是否与增益道具发生碰撞,如果发生碰撞则提高飞机子弹发射速率
示范图:

例题一代码:
<script type="text/javascript">
var app=new PIXI.Application(500,768);
document.body.appendChild(app.view);
function add(img,x,y){
//创建图片元素
var a=new PIXI.Sprite.fromImage(img);
//将元素添加到页面中
app.stage.addChild(a);
//调整位置
a.x=x
a.y=y
//对当前元素直接开启监听
a.interactive=true;
return a;
}
var bg = add("img/bg.jpg",0,0)
var plane=add("img/plane.png",250,450)
plane.anchor.set(0.5,0.5)
//记录帧频函数执行次数(计数器)
var count = 0
//记录帧频函数执行次数(计数器2)
var count2 = 0
//创建存储子弹元素的数组
var bs = [];
//创建存储exp元素的数组
var es = [];
//定义变量控制每等级生成子弹的时间
var ct = [60,50,40,30,20,15,5,3,1];
//飞机等级,-1用作下标获取对应生成子弹的时间
var level = 1;
//自定义帧频函数【核心代码】
//每秒生成一颗子弹,让子弹向上移动
//等级显示
var dengji = new PIXI.Text("等级:"+level)
dengji.style.fill = "#00000"
app.stage.addChild(dengji)
dengji.x = 350
dengji.y = 10
bg.on("mousemove" , planeMove );
//战机移动函数
function planeMove( event ){
var pos = event.data.getLocalPosition( app.stage );
plane.x = pos.x;
plane.y = pos.y;
bullet.x = pos.x;
bullet.y = pos.y;
}
function animate(){
//每3秒生成一个exp元素
if(count2 == 180){
// //添加图片元素(exp图标)
var xs=Math.random()*(500- 79 +1)
var exp = add("img/exp.png",xs,0);
//设置子弹图片锚点为中心
exp.anchor.set(0.5,0.5);
//在bs数组中添加子弹元素
es.push( exp );
//计数器归零
count2 = 0;
}
//每秒生成一颗子弹
if(count == ct[level-1]){
//添加图片元素(子弹)
var bullet = add("img/bullet_1.png",plane.x,plane.y);
//设置子弹图片锚点为中心
bullet.anchor.set(0.5,0.5);
//在bs数组中添加子弹元素
bs.push( bullet );
//计数器归零
count = 0;
}
//所有exp元素向下移动
for(var i=0; i<es.length; i++){
es[i].y += 3;
//如果exp与飞机发生碰撞
//飞机图片宽度:117
//exp图片宽度:79
//碰撞距离为:98((117/2) + (70/2))
var pos = 98;
//如果exp元素距离与飞机距离低于碰撞距离则发生碰撞
//获取 x 轴间距长度
var xl = plane.x - es[i].x;
//获取 y 轴间距长度
var yl = plane.y - es[i].y;
//碰撞判断
if(Math.sqrt(xl*xl+yl*yl)<=pos){
//提升飞机等级 ,并限制上限
if(level<=8)
level++;
//等级更新
dengji.text = "等级:"+level;
app.stage.addChild(dengji)
//计数器归零
count = 0;
//经验值图片消失
es[i].y = 768;
console.log( ct +" "+level);
}
//如果exp超出窗口,从数组中移除元素,从应用窗口中移除元素
if( es[i].y>=768 ){
//从应用窗口中移除元素
app.stage.removeChild( es[i] ); //从数组中移除元素
es.splice(i,1);
}
}
//子弹
for (var i = 0; i < bs.length; i++) {
var en = bs[i];
en.y -= 3;
//判断子弹是否超出边界
if(en.y < 0){
bs.splice(i,1);
//将元素从应用窗口中移除
app.stage.removeChild(en);
}
}
//帧频函数执行一次,计数器+1
count++;
count2++;
}
//添加帧频函数
app.ticker.add( animate )
</script>
运行结果:

例题二:子弹碰撞
1.创建添加应用,宽500,高700,添加相对应元素
2.设置飞机位置锚点,飞机跟随鼠标移动
3.定义变量,控制帧频函数执行频率
4.在逐帧函数中每秒钟创建敌机元素,将创建好的添加到数组中
5.编写子弹与敌机的碰撞逻辑。
示范图:

例题二代码:
<script type="text/javascript">
//创建窗口
var app = new PIXI.Application(500,700);
document.body.appendChild(app.view);
function add(img,x,y){
//减少程序冗余度,快速添加图片及位置
var a = new PIXI.Sprite.fromImage(img);
app.stage.addChild(a);
a.x=x;
a.y=y;
a.interactive = true;//打开对变量的监听
return a;
}
//添加元素
var bg = add("img/bg_01.png",0,0);
var bg01 = add("img/bg_01.png",0,-760);
var plane = add("img/plane_blue_01.png",200,200);
plane.anchor.set(0.5,0.5);
bg.on("mousemove" , planeMove );
//战机移动函数
function planeMove( event ){
var pos = event.data.getLocalPosition( app.stage );
plane.x = pos.x;
plane.y = pos.y;
bullet.x = pos.x;
bullet.y = pos.y;
}
//定时器
var count = 0
var escount = 0
//存储敌机的数组
var eslist = []
//存储子弹的数组
var bulletlist = [];
function bulletmove(){
//背景移动
bg.y += 1
if(bg.y == 760){
bg.y = 0
}
bg01.y = bg.y-760
//发射子弹速度
if(count == 30){
var bullet = add("img/bullet_1.png",plane.x,plane.y - 30);
bullet.anchor.set(0.5,0.5);
//将生成的子弹都存储到数组中。
bulletlist.push(bullet);
count = 0;
}
//60为一秒,每一秒随机生成一个x为0的敌机
if(escount == 60){
var enemy = add("img/enemy_02.png",Math.random()*450,0);
enemy.anchor.set(0.5,0.5);
eslist.push(enemy);
escount = 0;
}
//敌机
for (var ii = 0; ii < eslist.length; ii++) {
var enn = eslist[ii];
enn.y += 3
//判断敌机是否超出边界
if(enn.y > 700){
eslist.splice(ii,1);
app.stage.removeChild(enn);
}
}
//子弹
for (var i = 0; i < bulletlist.length; i++) {
var en = bulletlist[i];
en.y -= 3;
//判断子弹是否超出边界
if(en.y < 0){
bulletlist.splice(i,1);
//将元素从应用窗口中移除
app.stage.removeChild(en);
}
}
count += 1;
escount += 1;
//判断子弹与敌机是否相撞
var pos = 60
//遍历子弹列表
for (var i = 0; i < bulletlist.length; i++) {
//遍历敌机列表
for (var j = 0; j < eslist.length; j++) {
//x,y轴与子弹的距离
var y1 = bulletlist[i].y - eslist[j].y
var x1 = bulletlist[i].x - eslist[j].x
//判断是否小于概念⚪半径之和
if(y1*y1+x1*x1 < pos*pos){
//发生碰撞,移除子弹元素,移除列表中子弹元素
app.stage.removeChild(bulletlist[i])
bulletlist.splice(i,1);
app.stage.removeChild(eslist[j])
eslist.splice(j,1);
break
}
}
}
}
app.ticker.add(bulletmove);
</script>
运行结果:

本次文章所用到的图像以及源码:
https://download.csdn.net/download/qq_56376552/89427360
总结
本文 碰撞检测与定时动画 源码 就此结束,
如有错误或者网址更新,步骤更改,代码错误欢迎私信作者进行更正,
感谢您的观看。
更多推荐
所有评论(0)