演示效果
游戏机制
简单的击杀得分,躲避敌人攻击并击杀敌人获得经验升级和分数:
当还有剩余生命时通过按下q键复活,没有时显示最终分数:
当敌人被击中后会有0.5秒无敌时间,自己会有两秒无敌。升级会补满血量和能量。
敌人写了两种,一种是固定炮台,当玩家靠近会发射激光。另一种是黑色飞机,会在地图内巡逻,躲避玩家子弹和玩家并跟踪追击玩家:
wasd控制上下左右,通过鼠标左中右键发射不同子弹和c键发射激光:
类间关系与简易逻辑实现
其余ui,map等类就不介绍使用关系了。
敌人逻辑是根据自身血量判断,当血量大于1/2选择根据距离攻击或者追踪,与玩家过近即后退,否则躲避玩家,过远则继续游走。
炮台逻辑更简单,当距离在一定范围内蓄能发射激光,冷却时间内发射普通子弹。
主要代码
玩家类的显示和更新:
class Spaceship {
map mymap;
bullets all_bullet;
enemies enemies;
float laser_prepare_time=60;
float laser_prepare_timer=0;
float quick_shoot_timer=0;
float quick_shoot_interval=5;
float normal_shoot_timer=0;
float normal_shoot_interval=10;
boolean Ispreparing_laser=false;
ArrayList<life_variation_text> texts=new ArrayList();
float max_hp;
float max_mp;
float hp;
float mp;
float xp=0;
float xp_limit=800;
float damage=30;
int LV=1;
int life=3;
int score=0;
int protected_timer_limit=120;
int protected_timer=121;
float IsSprint;
// All of our regular motion stuff
PVector position;
PVector absolutePos;
PVector velocity;
PVector acceleration;
// Arbitrary damping to slow down ship
float damping = 0.99;
float topspeed = 6;
// Variable for heading!
float heading = 0;
// Size
float r = 16;
// Are we thrusting (to color boosters)
boolean thrusting = false;
bar bar=new bar(absolutePos);
Spaceship(map tmap,float hp,float mp,bullets all_bullet) {
this.hp=hp;
this.mp=mp;
max_hp=hp;
max_mp=mp;
this.all_bullet=all_bullet;
IsSprint=0;
position = new PVector(0,0);
velocity = new PVector();
acceleration = new PVector();
absolutePos=new PVector(0,0);
mymap=tmap;
}
Spaceship(map tmap,int hp,int mp,bullets all_bullet,enemies enemie)
{
this(tmap,hp,mp,all_bullet);
this.enemies=enemie;
}
// Standard Euler integration
void update() {
xp=xp>xp_limit?xp_limit:xp;
if(xp>=xp_limit)
{
levelup();
}
if(all_bullet.get_ship_laser_num()<1&&laser_prepare_timer>=laser_prepare_time&&mp>=50)
{
laser_prepare_timer=0;
}
mp+=0.03+(LV-1)*0.01;
mp=mp>max_mp?max_mp:mp;
velocity.add(acceleration);
velocity.mult(damping);
velocity.limit(topspeed);
position.add(velocity);
position.x=constrain(position.x,-mymap.size.x/2,mymap.size.x/2);
position.y=constrain(position.y,-mymap.size.y/2,mymap.size.y/2);
absolutePos=PVector.add(position.get(),mymap.pos.get());
if(protected_timer>=protected_timer_limit&&!IsDead())
{
getCollided();
getShooted();
IsSprint=acceleration.mag()>0?255:0;
}else
{
protected_timer++;
IsSprint=20;
}
acceleration.mult(0);
}
// Draw the ship
void display(float col,color c) {
rectMode(CENTER);
pushMatrix();
translate(absolutePos.x,absolutePos.y);
rotate(heading);
fill(c,IsSprint);
// Booster rockets
stroke(0);
strokeWeight(0);
rect(-r/3,r,r/3,r);
rect(r/3,r,r/3,r);
stroke(0);
strokeWeight(2);
if(protected_timer>=protected_timer_limit)
{
fill(col);
}else
{
fill(col,125);
}
// A triangle
beginShape();
vertex(-r,r);
vertex(0,-1.5*r);
vertex(r,r);
endShape(CLOSE);
rectMode(CENTER);
popMatrix();
show_hp_change();
thrusting = false;
}
}
子弹类:
class bullet
{
map tmap;
PVector pos;
PVector absolutePos;
PVector direct;
float speed;
float angle;
float damage;
color col;
bullet(PVector pos,float angle,map tmap,float damage,color col,float speed)
{
this.pos=pos;
this.speed=speed;
this.absolutePos=PVector.add(pos.get(),tmap.pos.get());
this.angle=angle;
this.direct=new PVector(sin(angle),-cos(angle));
this.tmap=tmap;
this.damage=damage;
this.col=col;
}
void display()
{
pushMatrix();
fill(col);
translate(pos.x,pos.y);
rotate(angle);
pos.add(direct.get().mult(speed));
rectMode(CENTER);
rect(0,0,3,30);
popMatrix();
}
void display_pos()
{
absolutePos=PVector.add(pos.get(),tmap.pos.get());
pushMatrix();
fill(col);
pos.add(direct.get().mult(speed));
translate(absolutePos.x,absolutePos.y);
rotate(angle);
rectMode(CENTER);
strokeWeight(1);
rect(0,0,3,30);
popMatrix();
}
}
激光类:
class laser
{
map mymap;
PVector Pos;
PVector absolutePos;
float angle;
float launch_time;
float launch_timer;
float duration=60;
float timer=0;
float max_distance=3000;
float max_radius;
float radius;
float distance=200;
float damage;
laser(PVector Pos,float radius,float angle,map mymap,float damage)
{
this.Pos=Pos;
this.mymap=mymap;
this.angle=angle;
this.max_radius=radius;
this.radius=max_radius/7;
this.damage=damage;
launch_time=max_radius-this.radius;
launch_timer=0;
absolutePos=PVector.add(Pos.get(),mymap.pos.get());
}
void display()
{
absolutePos=PVector.add(Pos.get(),mymap.pos.get());
launch_timer+=8;
if(launch_timer<launch_time)
{
radius+=8;
radius=radius>max_radius?max_radius:radius;
}
if(launch_timer>=launch_time&&timer<=duration)
{
timer++;
mymap.shake();
}else if(launch_timer>=launch_time)
{
radius-=4;
mymap.recover_from_shake();
}
radius=radius<0?0:radius;
distance+=100;
distance=distance>max_distance?max_distance:distance;
pushMatrix();
translate(absolutePos.x,absolutePos.y);
rotate(angle);
strokeWeight(radius);
stroke(60,0,0);
line(0,-radius/2,0,-distance);
popMatrix();
strokeWeight(1);
}
void display_enemy()
{
absolutePos=PVector.add(Pos.get(),mymap.pos.get());
launch_timer+=8;
if(launch_timer<launch_time)
{
radius+=8;
radius=radius>max_radius?max_radius:radius;
}
if(launch_timer>=launch_time&&timer<=duration)
{
timer++;
}else if(launch_timer>=launch_time)
{
radius-=4;
}
radius=radius<0?0:radius;
distance+=100;
distance=distance>max_distance?max_distance:distance;
pushMatrix();
translate(absolutePos.x,absolutePos.y);
rotate(angle);
strokeWeight(radius);
stroke(30);
line(0,-radius/2,0,-distance);
popMatrix();
}
float dist(PVector target)
{
PVector self=new PVector(sin(angle)*distance,-cos(angle)*distance);
PVector a=subpoint(target);
if(PVector.dot(PVector.sub(a,absolutePos),self)<0)
{
return 9999;
}else
{
return(abs(PVector.dist(subpoint(target),target)));
}
}
PVector subpoint(PVector target)
{
PVector self=new PVector(sin(angle)*distance,-cos(angle)*distance);
PVector start_target=new PVector(target.x-absolutePos.x,target.y-absolutePos.y);
PVector start_subpoint=self.normalize().mult(PVector.dot(start_target,self.normalize()));
return PVector.add(absolutePos.get(),start_subpoint);
}
}
构思与踩坑点
1.地图,玩家都是相对运动,单用独立坐标无法表示,分别用绝对坐标和相对坐标表示屏幕位置以及彼此关系。
2.敌人与玩家,地图都是相对运动,以地图中心点为(0,0)基准储存相对坐标。
3.子弹显示和击中不好管理所以创建了enemies类和bullets类分别保存应当显示的对应物体,当需要销毁时移除。
4.使用《代码本色》中介绍的随机游走会让物体一直贴着地图边缘走,改用地图中心为基础的随机目标点能比较好地模拟范围内随机移动。
缺陷
1.当地图随着飞机移动时有时候会有卡顿
2.应当考虑将地图分块,考虑同一地图块内物体间的碰撞,每次都遍历整个子弹列表或者敌人列表速度很慢,所以没考虑敌人之间相互避免碰撞。
3.敌人总是扎堆生成,应考虑新的随机方法。
运用到的《代码本色》中相关内容
1.ch0 随机数
2.ch1 向量,速度,加速度
3.ch2 力
4.ch6 自治智能体
参考文章
【1】行为树和Behavior Designer插件https://zhuanlan.zhihu.com/p/29366677
【2】《代码本色》https://natureofcode.com/
同学作品推荐
1 作者:吴彦宏
《Magic Network》-使用processing编写的神经网络交互系统,可以方便地创建任意层数和节点的全连接网络,同时可以观察到运行过程中的结果,可以帮助使用者理解深度神经网络的结构。
2.作者:周际翔
《Gravity》-使用openFramework编写的游戏,画面清新,游戏操作简单,可玩性强,ui精美,支持手柄操作
3作者:吕天旭
《动态条形码》-以条形码作为创意点,在简单的黑白条形码上进行艺术加工,可以将条形码作为画板,也有着各种各样的交互方式,看起来十分精美。