地图上现在有其他角色. 他能在某个范围内四处乱走.
主角会撞到NPC上.
思路:
– NPC是能移动的障碍物, 位置的基准是点, 所以由前例的圆形障碍物继承.
– NPC的描画和判定同障碍物, 但走动需要另外处理, 所以还需要一个NPC容器.
– 如果NPC走动后判定范围和主角重叠会导致主角卡死, 所以要想办法把主角位置信息告知NPC类.(这里直接把主角引用包在地图库里在调用走动方法时当参数传递)
– NPC在感知到主角接近后不再走动, 转而面向主角, 待主角走远后再返回走动状态.
[W|S|A|D]:移动角色
[Q]:退出程序
在2.0内制作, 用3.0以上运行可能会报错:
//- --- --- ---
//- public
//- --- --- ---
//-
EcMap pbTheCurrentMap=null;
EcProtagon pbTheKid=new EcProtagon();
//--
EcMap pbThisMapAA=new EcMap(800,400,311,pbTheKid);
EcMap pbThisMapAB=new EcMap(400,200,312,pbTheKid);
EcMap pbThisMapAC=new EcMap(400,1450,313,pbTheKid);
//--
EcTrangleObstacle pbThatDoorAAL=new EcTrangleObstacle(730, 350, 20, 20,'b');
EcTrangleObstacle pbThatDoorAAR=new EcTrangleObstacle(750, 350, 20, 20,'d');
EcTrangleObstacle pbThatDoorABL=new EcTrangleObstacle(30,150, 20, 20,'c');
EcTrangleObstacle pbThatDoorABR=new EcTrangleObstacle(50, 150, 20, 20,'a');
EcRectangleObstacle pbThatDoorBA=new EcRectangleObstacle(150, 100, 30, 10);
EcRectangleObstacle pbThatDoorBB=new EcRectangleObstacle(350-15, 1440, 30, 10);
//--
EcSomeKid pbThatKidAA = new EcSomeKid(150, 150, 40, 40, "ness");
//-
@Override
public void setup() {size(320, 240);noStroke();frameRate(32);textAlign(LEFT, TOP);ellipseMode(CENTER);
frame.setTitle("CharaMap");
//--
pbThatDoorAAL.cmOffColor=color(0xEE,0x55,0x55);pbThatDoorAAL.cmID=11;
pbThatDoorAAR.cmOffColor=color(0xEE,0x55,0x55);pbThatDoorAAR.cmID=11;
pbThatDoorABL.cmOffColor=color(0xEE,0x55,0x55);pbThatDoorABL.cmID=12;
pbThatDoorABR.cmOffColor=color(0xEE,0x55,0x55);pbThatDoorABR.cmID=12;
pbThatDoorBA.cmOffColor=color(0xEE,0x55,0x55);pbThatDoorBA.cmID=21;
pbThatDoorBB.cmOffColor=color(0xEE,0x55,0x55);pbThatDoorBB.cmID=22;
//--
pbThisMapAA.ccAddObstacle(new EcRectangleObstacle(0, 0, 800, 300));
pbThisMapAA.ccAddObstacle(new EcTrangleObstacle(0, 280, 120, 120,'a'));
pbThisMapAA.ccAddObstacle(new EcTrangleObstacle(680, 280, 120, 120,'b'));
pbThisMapAA.ccAddObstacle(new EcRectangleObstacle(150, 295, 60, 27));
pbThisMapAA.ccAddObstacle(new EcRectangleObstacle(450, 295, 60, 27));
pbThisMapAA.ccAddObstacle(new EcRoundObstacle(350, 350, 50));
pbThisMapAA.ccAddHotspot(pbThatDoorAAL);
pbThisMapAA.ccAddHotspot(pbThatDoorAAR);
//--
pbThisMapAB.ccAddObstacle(new EcRectangleObstacle(0, 0, 400, 100));
pbThisMapAB.ccAddObstacle(new EcTrangleObstacle(0, 100, 100, 100,'a'));
pbThisMapAB.ccAddObstacle(new EcTrangleObstacle(300, 100, 100, 100,'b'));
pbThisMapAB.ccAddObstacle(new EcRectangleObstacle(210, 80, 47, 60));
pbThisMapAB.ccAddObstacle(new EcRectangleObstacle(230, 120, 43, 60));
pbThisMapAB.ccAddObstacle(new EcRectangleObstacle(198, 168, 50, 35));
pbThisMapAB.ccAddHotspot(pbThatDoorABL);
pbThisMapAB.ccAddHotspot(pbThatDoorABR);
pbThisMapAB.ccAddHotspot(pbThatDoorBA);
pbThisMapAB.ccAddNPC(pbThatKidAA);
//--
pbThisMapAC.ccAddObstacle(new EcRectangleObstacle(0, 0, 300, 450));
pbThisMapAC.ccAddObstacle(new EcRectangleObstacle(0, 450, 200, 600));
pbThisMapAC.ccAddObstacle(new EcRectangleObstacle(0, 450+600, 300, 400));
pbThisMapAC.ccAddObstacle(new EcRectangleObstacle(400, 0, 200, 1450));
pbThisMapAC.ccAddObstacle(new EcRectangleObstacle(300,550, 100, 400));
pbThisMapAC.ccAddObstacle(new EcRectangleObstacle(300,0, 100, 50));
pbThisMapAC.ccAddHotspot(pbThatDoorBB);
//--
fsLoadMap(pbThisMapAA,-300,-200,400,357);
pbTheKid.ccMoveCharactor(1f, 0);
//--
}//+++
@Override
public void draw(){background(0);
//--
if(pbTheCurrentMap!=null){
pbTheCurrentMap.ccUpdate();
spDrawMap(pbTheCurrentMap);
}
pbTheKid.ccUpdate();
}//+++
@Override
public void keyPressed() {
int lpSpotFlag=0;
switch(key){
//--
case 'w':lpSpotFlag=pbTheKid.ccMoveCharactor(0, -1f);pbTheKid.cmHeading=2*PI;break;
case 's':lpSpotFlag=pbTheKid.ccMoveCharactor(0, 1f);pbTheKid.cmHeading=PI;break;
case 'a':lpSpotFlag=pbTheKid.ccMoveCharactor(-1f, 0);pbTheKid.cmHeading=PI*3/2;break;
case 'd':lpSpotFlag=pbTheKid.ccMoveCharactor(1f, 0);pbTheKid.cmHeading=PI/2;break;
//--
case 'q':fsPover();
default:break;
}
//--
switch(lpSpotFlag){
case 11:
fsLoadMap(pbThisMapAB, 0, 0, 75, 160);pbTheKid.ccMoveCharactor(1f, 0);
break;
case 12:
fsLoadMap(pbThisMapAA, 0, 0, 720, 360);pbTheKid.ccMoveCharactor(-1f, 0);
break;
case 21:
fsLoadMap(pbThisMapAC, 200, 1500, 350, 1420);pbTheKid.ccMoveCharactor(0, -1f);
break;
case 22:
fsLoadMap(pbThisMapAB, 0, 0, 165, 115);pbTheKid.ccMoveCharactor(0, 1f);
break;
default:break;
}
//--
}//+++
//< <<< <<< <<< <<< <<< Overrided
//* *** *** *** *** ***
//*
//* Operate
//*
//* *** *** *** *** ***
//- --- --- ---
void fsPover(){
exit();
}//+++
//< <<< <<< <<< <<< <<< operate
//* *** *** *** *** ***
//*
//* Support
//*
//* *** *** *** *** ***
//- --- --- ---
void fsLoadMap(EcMap pxMap,int pxMapCameraX,int pxMapCameraY, int pxCharaStartX, int pxCharaStartY){
pbTheCurrentMap=pxMap;
pbTheKid.ccInit(pxMap,pxCharaStartX,pxCharaStartY);
pxMap.ccRepos(-pxMapCameraX, -pxMapCameraY);
}//+++
//< <<< <<< <<< <<< <<< Support
//* *** *** *** *** ***
//*
//* drawMap
//*
//* *** *** *** *** ***
//- --- --- ---
//- gate
//- --- --- ---
void spDrawMap(EcMap pxMap){
switch(pxMap.cmID){
case 311:spDrawMapAA(pxMap.cmPos.x,pxMap.cmPos.y);break;
case 312:spDrawMapAB(pxMap.cmPos.x,pxMap.cmPos.y);break;
case 313:spDrawMapAC(pxMap.cmPos.x,pxMap.cmPos.y);break;
default:break;
}
}//+++
//- --- --- ---
//- indie
//- --- --- ---
void spDrawMapAA(float pxX, float pxY){
stroke(0xFF);noFill();{
rect(pxX+150,pxY+295,60,27); //...chair
rect(pxX+450,pxY+295,60,27);//...another chair
ellipse(pxX+350,pxY+350,50,50);//...table
quad(pxX+0,pxY+0,pxX+100,pxY+0,//...wall a
pxX+100,pxY+300,pxX+0,pxY+400);
quad(pxX+700,pxY+0, pxX+800,pxY+0,//...wall b
pxX+800,pxY+400, pxX+700,pxY+300 );
quad(pxX+750,pxY+300, pxX+770,pxY+315,//...door
pxX+770,pxY+370, pxX+750,pxY+350 );
}noStroke();
fill(0xFF);
text("chair",pxX+455,pxY+305);
text("another\n chair",pxX+155,pxY+292);
text("table",pxX+340,pxY+340);
text("wall...should there be a window??",pxX+400,pxY+260);
text("wall",pxX+50,pxY+300);
text("wall...\n with a door",pxX+720,pxY+255);
}//+++
void spDrawMapAB(float pxX, float pxY){
stroke(0xFF);noFill();{
quad(pxX+0,pxY+0,pxX+100,pxY+0,//...wall a
pxX+100,pxY+100,pxX+0,pxY+200);
quad(pxX+300,pxY+0,pxX+400,pxY+0,//...wall b
pxX+400,pxY+200,pxX+300,pxY+100);
rect(pxX+210,pxY+80,47,60); //...box a
rect(pxX+230,pxY+120,43,60); //...box b
rect(pxX+198,pxY+168,50,35); //...box c
rect(pxX+150,pxY+100,30,-50); //...door to room c
quad(pxX+30,pxY+115,pxX+50,pxY+100,//...door to room a
pxX+50,pxY+150,pxX+30,pxY+170);
}noStroke();
fill(0xFF);
text("still...\n a wall\n \n [back!!]",pxX+20,pxY+45);
text("its a longway\n to the top \n if you wanna rock n roll!!",pxX+120,pxY+5);
text("some\n box",pxX+215,pxY+85);
text("box..",pxX+235,pxY+140);
text("Dont\n ask!!",pxX+200,pxY+170);
text("the\n wall\n youll\n never\n reach",pxX+305,pxY+35);
}//+++
void spDrawMapAC(float pxX, float pxY){
stroke(0xFF);noFill();{
//-- ** ...actually there is nothing in room c
}noStroke();
fill(0xFF);
text("rock n...roll?",pxX+315,pxY+15);
}//+++
//< <<< <<< <<< <<< <<< drawMap
//* *** *** *** *** ***
//*
//* Class
//*
//* *** *** *** *** ***
//- --- --- ---
//- charactor and map
//- --- --- ---
class EcMap{
//--
EcProtagon cmProtagon;
//--
PVector cmPos, cmDia;
ArrayList<EcObstacle> cmObsatcleList;
ArrayList<EcObstacle> cmHotspotList;
ArrayList<EcNPCharactor> cmNPCList;
float cmCameraRange;
//--
int cmID;
//--
EcMap(int pxW,int pxH, int pxID, EcProtagon pxProtagon){
cmPos=new PVector(0,0);
cmDia=new PVector(pxW, pxH);
cmObsatcleList=new ArrayList<EcObstacle>();
cmHotspotList=new ArrayList<EcObstacle>();
cmNPCList=new ArrayList<EcNPCharactor>();
cmCameraRange=100;
cmID=pxID;
cmProtagon=pxProtagon;
}
//--
void ccRepos(int pxX, int pxY){
cmPos.x=(float)pxX;
cmPos.y=(float)pxY;
}
//--
void ccAddObstacle(EcObstacle pxObstacle){cmObsatcleList.add(pxObstacle);}
void ccAddHotspot(EcObstacle pxSpot){cmHotspotList.add(pxSpot);}
void ccAddNPC(EcNPCharactor pxCharactor){
cmObsatcleList.add(pxCharactor);
cmNPCList.add(pxCharactor);
}
//--
void ccUpdate(){
fill(0xEE,0xAA,0x33);rect(cmPos.x,cmPos.y,cmDia.x,cmDia.y);
if(!cmObsatcleList.isEmpty()){for(EcObstacle itObstacle:cmObsatcleList){
itObstacle.ccRepos((int)cmPos.x, (int)cmPos.y);
itObstacle.ccUpdate();
}}
if(!cmHotspotList.isEmpty()){for(EcObstacle itObstacle:cmHotspotList){
itObstacle.ccRepos((int)cmPos.x, (int)cmPos.y);
itObstacle.ccUpdate();
}}
if(!cmNPCList.isEmpty()){for(EcNPCharactor itCharactor:cmNPCList){
itCharactor.ccWander(cmProtagon.cmRelatePos);
}}
}
//--
void ccMoveCamera(float pxX, float pxY){
float lpRangeX=cmCameraRange;
float lpRangeY=cmCameraRange;
float lpRangeW=width-(cmCameraRange*2);
float lpRangeH=height-(cmCameraRange*2);
if(pxX<=lpRangeX){cmPos.x+=(lpRangeX-pxX);}
if(pxX>=(lpRangeX+lpRangeW)){cmPos.x-=(pxX-lpRangeX-lpRangeW);}
if(pxY<=lpRangeY){cmPos.y+=(lpRangeY-pxY);}
if(pxY>=(lpRangeY+lpRangeH)){cmPos.y-=(pxY-lpRangeY-lpRangeH);}
}
//--
boolean ccIsInMovableRange(float pxX, float pxY){
if(
(pxX>cmPos.x)&&(pxX<cmPos.x+cmDia.x)&&(pxY>cmPos.y)&&(pxY<cmPos.y+cmDia.y)
){
if(!cmObsatcleList.isEmpty()){for(EcObstacle itObstacle:cmObsatcleList){
boolean lpRes=itObstacle.ccIsContaining((int)pxX, (int)pxY);
if(lpRes){return false;}
}}
return true;
}
return false;
}
//--
int ccTellEnvID(float pxX, float pxY){
if(!cmHotspotList.isEmpty()){for(EcObstacle itObstacle:cmHotspotList){
if(itObstacle.ccIsContaining((int)pxX, (int)pxY)){
return itObstacle.cmID;
}
}}
return 0;
}
//--
}//+++
class EcProtagon{
EcMap cmMap;
PVector cmRelatePos;
//--
float cmSpeed;
float cmHeading;
int cmFoot;
//--
int cmEnvID;
//--
EcProtagon(){
cmMap=null;
cmRelatePos=new PVector(0, 0);
cmSpeed=3.3f;
cmHeading=PI/2;
cmFoot=0;cmEnvID=0;
}
//--
void ccInit(EcMap pxMap, int pxX, int pxY){
cmMap=pxMap;
cmRelatePos.x=(float)pxX;
cmRelatePos.y=(float)pxY;
}
//--
void ccUpdate(){
if(cmMap==null){return;}
int lpAbsoX=(int)(cmRelatePos.x+cmMap.cmPos.x);
int lpAbsoY=(int)(cmRelatePos.y+cmMap.cmPos.y);
//--
fill(0xFF);stroke(0);pushMatrix();{
translate(lpAbsoX, lpAbsoY);
rotate(cmHeading);
quad(
-6,-10,
+6,-10,
+12,+10,
-12,+10
);
fill(cmFoot<3?0x33:0xCC);rect(2,2,4,4);
fill(cmFoot<3?0xCC:0x33);rect(-6,2,4,4);
//fill(0xff);text(nf(cmEnvID,2),3,3);//<<[DTFM]
}popMatrix();noStroke();
//--
}
//--
int ccMoveCharactor(float pxX, float pxY){
float lpNewPosX=cmRelatePos.x+pxX*cmSpeed;
float lpNewPosY=cmRelatePos.y+pxY*cmSpeed;
if(cmMap.ccIsInMovableRange(lpNewPosX+cmMap.cmPos.x,lpNewPosY+cmMap.cmPos.y)){
cmFoot++;cmFoot&=0x07;
cmRelatePos.x=lpNewPosX;
cmRelatePos.y=lpNewPosY;
cmMap.ccMoveCamera(lpNewPosX+cmMap.cmPos.x, lpNewPosY+cmMap.cmPos.y);
cmEnvID=cmMap.ccTellEnvID(lpNewPosX+cmMap.cmPos.x, lpNewPosY+cmMap.cmPos.y);
return cmEnvID;
}
return 0;
}
}//+++
//- --- --- ---
//- Obsatcle
//- --- --- ---
abstract class EcObstacle{
int cmFollowX,cmFollowY;
int cmAbsoX,cmAbsoY;
int cmX,cmY,cmW,cmH;
int cmOffColor,cmOnColor;
boolean cmAct;
//--
int cmID;
//--
EcObstacle(){
cmX=cmY=cmW=cmH=9;
cmOffColor=color(0x55,0x55,0x55);
cmOnColor=color(0xEE,0xEE,0x33);
cmAct=false;
cmFollowX=0;cmFollowY=0;
cmAbsoX=cmFollowX+cmX;cmFollowY=cmFollowY+cmY;
cmID=255;
}
//--
void ccRepos(int pxX, int pxY){
cmFollowX=pxX;
cmFollowY=pxY;
}
void ccRefreshPos(){
cmAbsoX=cmFollowX+cmX;
cmAbsoY=cmFollowY+cmY;
}
//--
abstract void ccUpdate();
abstract boolean ccIsContaining(int pxX, int pxY);
}//+++
class EcRectangleObstacle extends EcObstacle{
//--
EcRectangleObstacle(int pxX, int pxY, int pxW, int pxH) {
super();
cmX=pxX;cmY=pxY;
cmW=pxW;cmH=pxH;
}
//--
@Override void ccUpdate(){
ccRefreshPos();
fill(cmAct?cmOnColor:cmOffColor);
rect(cmAbsoX,cmAbsoY,cmW,cmH);
}
//--
@Override boolean ccIsContaining(int pxX, int pxY){
return (pxX>cmAbsoX)&&(pxX<cmAbsoX+cmW)&&(pxY>cmAbsoY)&&(pxY<cmAbsoY+cmH);
}
//--
}//+++
class EcRoundObstacle extends EcObstacle{
//--
int cmR;
//--
EcRoundObstacle(int pxX, int pxY, int pxD){
super();
cmX=pxX;cmY=pxY;
cmW=cmH=pxD;
cmR=pxD/2;
}
//--
@Override void ccUpdate(){
ccRefreshPos();
fill(cmAct?cmOnColor:cmOffColor);
ellipse(cmAbsoX,cmAbsoY,cmW,cmH);
}
//--
@Override boolean ccIsContaining(int pxX, int pxY){
if(!((pxX>cmAbsoX-cmR)&&(pxX<cmAbsoX+cmR)&&(pxY>cmAbsoY-cmR)&&(pxY<cmAbsoY+cmR))){return false;}else{
PVector lpCenter=new PVector(cmAbsoX,cmAbsoY);
PVector lpTarget=new PVector(pxX,pxY);
return ceil(PVector.dist(lpTarget,lpCenter))<cmR;
}
}
}//+++
class EcTrangleObstacle extends EcObstacle{
//--
char cmMode;
/** *
* @param pxMode_ab [a]north west point..[b]north east point..
* [c]south east point..[d]south west point
*/
EcTrangleObstacle(int pxX, int pxY, int pxW, int pxH, char pxMode_abcd){
super();
cmX=pxX;cmY=pxY;
cmW=pxW;cmH=pxH;
cmMode=pxMode_abcd;
}
//--
@Override void ccUpdate(){
ccRefreshPos();
fill(cmAct?cmOnColor:cmOffColor);
switch(cmMode){
case 'a':triangle(cmAbsoX, cmAbsoY, cmAbsoX+cmW, cmAbsoY, cmAbsoX, cmAbsoY+cmH);break;
case 'b':triangle(cmAbsoX, cmAbsoY, cmAbsoX+cmW, cmAbsoY, cmAbsoX+cmW, cmAbsoY+cmH);break;
case 'c':triangle(cmAbsoX+cmW, cmAbsoY, cmAbsoX+cmW, cmAbsoY+cmH, cmAbsoX, cmAbsoY+cmH);break;
case 'd':triangle(cmAbsoX, cmAbsoY, cmAbsoX+cmW, cmAbsoY+cmH, cmAbsoX, cmAbsoY+cmH);break;
default:break;
}
}
//--
@Override boolean ccIsContaining(int pxX, int pxY){
if(!((pxX>cmAbsoX)&&(pxX<cmAbsoX+cmW)&&(pxY>cmAbsoY)&&(pxY<cmAbsoY+cmH))){return false;}else{
int lpDPointL=(pxX-cmAbsoX)*cmH/cmW+cmAbsoY;
int lpDPointR=(cmAbsoX+cmW-pxX)*cmH/cmW+cmAbsoY;
switch(cmMode){
case 'a':return pxY<lpDPointR;
case 'b':return pxY<lpDPointL;
case 'c':return pxY>lpDPointR;
case 'd':return pxY>lpDPointL;
default:return false;
}
}
}
//--
}//+++
class EcNPCharactor extends EcRoundObstacle{
String cmName;
int cmClock;
//--
int cmRangeX, cmRangeY;
int cmRangeW, cmRangeH;
//--
int cmSpeed,cmFoot;
float cmHeading;
//--
EcNPCharactor(int pxX, int pxY,int pxRangeW, int pxRangeH, String pxName) {
super(pxX, pxY,32);
cmName=pxName;
cmClock=ceil(random(1,62));
cmSpeed=2;cmFoot=3;
cmHeading=PI/2;
cmRangeW=pxRangeW;
cmRangeH=pxRangeH;
cmRangeX=cmX-cmRangeW/2;
cmRangeY=cmY-cmRangeH/2;
}
//--
void ccWander(PVector pxProtagonPos){
cmClock++;cmClock&=0x1F;
if(cmClock==17||cmClock==30){
if(random(1)<0.51){
PVector lpPos=new PVector(cmX,cmY);
if(PVector.dist(pxProtagonPos,lpPos)<((float)cmR*1.3f)){
cmHeading=PVector.sub(pxProtagonPos,lpPos).heading()+PI/2;
return;
}
boolean lpDirectUD=random(1)<0.51;
boolean lpDirectLR=random(1)<0.51;
cmY+=cmSpeed*(lpDirectUD?-1:1);
cmX+=cmSpeed*(lpDirectLR?-1:1);
cmHeading=PI*(lpDirectUD?1:2);
cmHeading=PI*(lpDirectLR?(1/2):(3/2));
cmFoot++;cmFoot&=0x07;
cmX=constrain(cmX, cmRangeX, cmRangeX+cmRangeW);
cmY=constrain(cmY, cmRangeY, cmRangeY+cmRangeH);
}
}
}
}//+++
class EcSomeKid extends EcNPCharactor{
EcSomeKid(int pxX, int pxY,int pxRangeX, int pxRangeY, String pxName) {
super(pxX, pxY,pxRangeX,pxRangeY,pxName);
}
//--
@Override void ccUpdate(){
ccRefreshPos();
//--
fill(0xCC);stroke(0);pushMatrix();{
translate(cmAbsoX, cmAbsoY);
rotate(cmHeading);
quad(
-5,-8,
+5,-8,
+10,+8,
-10,+8
);
fill(cmFoot<3?0x33:0xCC);rect(2,2,4,4);
fill(cmFoot<3?0xCC:0x33);rect(-6,2,4,4);
}popMatrix();noStroke();
}
//--
}//+++
//< <<< <<< <<< <<< <<< Class
//EOF
- 本文固定链接: http://iprocessing.cn/2017/12/15/习作ce_有其他角色的场景地图/
- 转载请注明: constrain 于 Processing编程艺术 发表