nesdev.com
http://forums.nesdev.com/

关于碰撞检测/About collision detection
http://forums.nesdev.com/viewtopic.php?f=18&t=9717
Page 1 of 1

Author:  rebeLdanceR [ Fri Jan 18, 2013 7:21 pm ]
Post subject:  关于碰撞检测/About collision detection

当我成功的完成了我的碰撞检测算法后,真心想跟大家分享一下,希望大家多给些意见,让这个算法更加完善。
首先解释一下代码中用到的变量:
$0D $0E $14 $15 $16 $17 $18 $19 (Sp_addr1~Sp_addr4)
这八个内存里面分别保存了4个ROM区的16位内存地址(低8为+高8位),用来记录player精灵当前所在的位置(最少占1格,最多占4格)。
思路就是将精灵的位置映射到地图中,地图数据保存在命名表,命名表的数据又是从ROM中提取出来的,所以直接将精灵的位置映射到ROM了。
$13 $0F (Xp,Yp)
分别保存了精灵的水平和垂直的偏移量(值都是在F9~07间变化)。当精灵在某个方向上移动时,每8像素后便完全进入了另一个tile,所以这两个偏移量将会区分精灵是从上下左右哪个方向进入tile。而从不同的方向进入将会有不同的方法改变精灵当前在ROM中映射的内存地址。
代码中用到的方法:
change_addr()
当精灵从不同的方向完全进入一个tile的时候(通过对Xp,Yp的值来进行判断),它在ROM中所映射的地址将会有所改变(用以对未来前进方向是否可以通行进行判断)。
xx_on()
对应四个按钮的触发事件。
get(Sp_addr)
获取精灵当前映射到的地址。
getID(Sp_addr)
获取精灵所映射的地址中保存的tile ID。
=========================
代码:
//==================
change_addr(){
if(Xp==0 && Yp!=0){
if(Yp>8){
Sp_addr2=Sp_addr1+$20
}
if(Yp<8){
Sp_addr2=Sp_addr1-$20
}
}
if(Xp!=0 && Yp==0){
if(Xp>8){
Sp_addr2=Sp_addr1+$1
}
if(Xp<8){
Sp_addr2=Sp_addr1-$1
}
}
}
//================
//===================
Up_on(){
change_addr();
if(Yp==$07){
Sp_addr1-=$20;
Yp=$00;
Sy--;
return;
}
if(Xp==0 && Yp==0){
get(Sp_addr1);
Sp_addr2=Spaddr_1-$20;
if((getID(Sp_addr2)-$82)<0){
Yp++;
Sy--;
return;
}else{
return;
}
}
if(Xp==0 && Yp!=0){
Yp++;
Sy--;
return;
}
if(Xp!=0 && Yp==0){
get(Sp_addr1);
Sp_addr3=Spaddr_1-$20;
if((getID(Sp_addr3)-$82)<0){
get(Sp_addr2);
Sp_addr4=Spaddr_2-$20;
if((getID(Sp_addr4)-$82)<0){
Yp++;
Sy--;
return;
}else{
return;
}
}else{
return;
}
}
if(Xp!=0 && Yp!=0){
Yp++;
Sy--;
return;
}
}
//===================
//====================
Down_on(){
change_addr();
if(Yp==$F9){
Sp_addr1+=$20;
Yp=$00;
Sy++;
return;
}
if(Xp==0 && Yp==0){
get(Sp_addr1);
Sp_addr2=Spaddr_1+$20;
if((getID(Sp_addr2)-$82)<0){
Yp--;
Sy++;
return;
}else{
return;
}
}
if(Xp==0 && Yp!=0){
Yp--;
Sy++;
return;
}
if(Xp!=0 && Yp==0){
get(Sp_addr1);
Sp_addr3=Spaddr_1+$20;
if((getID(Sp_addr3)-$82)<0){
get(Sp_addr2);
Sp_addr4=Spaddr_2+$20;
if((getID(Sp_addr4)-$82)<0){
Yp--;
Sy++;
return;
}else{
return;
}
}else{
return;
}
}
if(Xp!=0 && Yp!=0){
Yp--;
Sy++;
return;
}
}
//======================
//======================
Left_on(){
change_addr();
(Xp==$07){
Sp_addr1-=$01;
Xp=$00;
Sx--;
return;
}
if(Xp==0 && Yp==0){
get(Sp_addr1);
Sp_addr2=Spaddr_1-$01;
if((getID(Sp_addr2)-$82)<0){
Xp++;
Sx--;
return;
}else{
return;
}
}
if(Xp!=0 && Yp==0){
Xp++;
Sx--;
return;
}
if(Xp==0 && Yp!=0){
get(Sp_addr1);
Sp_addr3=Spaddr_1-$01;
if((getID(Sp_addr3)-$82)<0){
get(Sp_addr2);
Sp_addr4=Spaddr_2-$01;
if((getID(Sp_addr4)-$82)<0){
Xp++;
Sx--;
return;
}else{
return;
}
}else{
return;
}
}
if(Xp!=0 && Yp!=0){
Xp++;
Sx--;
return;
}
}
//======================
//======================
Right_on(){
change_addr();
(Xp==$F9){
Sp_addr1+=$01;
Xp=$00;
Sx++;
return;
}
if(Xp==0 && Yp==0){
get(Sp_addr1);
Sp_addr2=Spaddr_1+$01;
if((getID(Sp_addr2)-$82)<0){
Xp--;
Sx++;
return;
}else{
return;
}
}
if(Xp!=0 && Yp==0){
Xp--;
Sx++;
return;
}
if(Xp==0 && Yp!=0){
get(Sp_addr1);
Sp_addr3=Spaddr_1+$01;
if((getID(Sp_addr3)-$82)<0){
get(Sp_addr2);
Sp_addr4=Spaddr_2+$01;
if((getID(Sp_addr4)-$82)<0){
Xp--;
Sx++;
return;
}else{
return;
}
}else{
return;
}
}
if(Xp!=0 && Yp!=0){
Xp--;
Sx++;
return;
}
}
//======================

Author:  rebeLdanceR [ Fri Jan 18, 2013 9:10 pm ]
Post subject:  Re: 关于碰撞检测/About collision detection

上游戏文件,仿作的“世界最难小游戏”,仅有第一关。player与地图之间的碰撞和player与CPU精灵之间的碰撞算法是不同的。不过对于多个精灵之间的碰撞检测还没有完善,目前的算法只适用于少数精灵之间的碰撞(通过对比精灵4个边角的坐标)。

Attachments:
d_20130118.nes [40.02 KiB]
Downloaded 611 times

Author:  rebeLdanceR [ Sat Jan 19, 2013 11:42 pm ]
Post subject:  Re: 关于碰撞检测/About collision detection

我的这个碰撞检测方法仅适用于tile级别的碰撞检测。
精灵初始化后,映射在ROM中的内存地址会保存在Sp_addr1,水平和垂直偏移量会置0。
所以精灵所在的tile将会有3种状态:仅占1个tile位置,此时水平偏移量Xp,垂直偏移量Yp均为0;
占2个tile位置,此时会是Xp==0&&Yp!=0,或者Xp!=0&&Yp==0,对应垂直方向占2格,水平方向占2格;
占4个tile位置,Xp!=0&&Yp!=0。
但是精灵的移动是按像素来的,所以将会在精灵完全进入下一个tile中后,改变其在ROM中映射的内存地址,这由每个按键事件的第一个判断语句来实现。
精灵在移动的过程中将会在这3中情况中变换。每种情况有不同的检测方法,这取决于精灵所占的tile情况,和将要移动的方向。
我花费了大量的时间去演算所有在精灵移动中可能发生的情况,它是大量的,我在试图描述它们的过程中遇到了很多困扰。终于在第四天的时候想到了一个充分描述它们的办法。
算法的核心在于如何随着精灵的移动去改变其映射在ROM中的内存地址,这由change_addr方法进行实现。
当精灵处于占1个tile的状态时,如果成功向按键方向进行了移动,将会把前进方向在ROM中映射的内存地址写入Sp_addr2。
当精灵处于占2个tile的状态时,会有四种情况:
Xp==0&&Yp!=0{向垂直方向移动是向占1tile的状态变化,无需碰撞检测;向水平方向移动时将会触发碰撞检测}
Xp!=0&&Yp==0{向水平方向移动是向占1tile的状态变化,无需碰撞检测;向垂直方向移动时将会触发碰撞检测}
当精灵处于占4个tile的状态时,无需进行碰撞检测,因为检测发生在进入这种状态之前,所以已经进入后就可以随意移动了。
而在精灵从占4个tile的状态变化成占2个tile的状态时,将对它在ROM中映射的内存地址(Sp_addr1,Sp_addr2)进行改变。
以上就完成了对精灵移动的完整描述,如果您发现了Bug,希望及时联系我,在此表示感谢。

Page 1 of 1 All times are UTC - 7 hours
Powered by phpBB® Forum Software © phpBB Group
http://www.phpbb.com/