8.6 原型Ship类
PrototypeShip代码位于javagames.prototype包中,这也是一目了然的。构造方法为移动飞船设置了一些常量,并且直接编码了模型的点。还有set()和get()方法用于销毁状态、角度、加速等,还有一些方法能够向左或向右旋转飞船。
launchBullet()方法返回一个新的PrototypeBullet对象,该对象转换为飞船的突起部分。
// PrototypeShip.java
public PrototypeBullet launchBullet() {
Vector2f bulletPos = position.add( Vector2f.polar( angle, 0.0325f ) );
return new PrototypeBullet( bulletPos, angle );
}```
isTouching()方法检查渲染列表中的每一艘飞船与给定的小行星物体之间的碰撞。最酷的部分是updatePosition()方法,它使得飞船来回飞行。首先,要更新速度:
V__1=V__0+at
加速度在setThrusting()方法中设置。如果飞船没有向前移动,那么加速度为0并且速度保持不变。注意,新的加速度向量使用当前的角度,这会慢慢改变飞船航行的方向。
Vector2f accel = Vector2f.polar(angle, curAcc);`
接下来,新的速度保持在最大值之下。如果没有执行这一步,飞船会持续加速并且最终会比子弹移动得还要快。注意这里使用了最小值,因此当最大速度不再比实际速度小的时候,最大速度为1.0并且保持不变。只有在最大速度小于实际值的时候,速度才会受到有效的限制。
float maxSpeed = Math.min( maxVelocity / velocity.len(), 1.0f );
velocity = velocity.mul( maxSpeed );```
接下来,应用了摩擦力。即便太空中没有摩擦力,我还是想给飞船添加摩擦力,以便它最终能够减速。如果飞船没有加速,摩擦力将会使其慢下来。
float slowDown = 1.0f - friction * time;
velocity = velocity.mul( slowDown );`
最后,更新位置,然后折返。
position = position.add( velocity.mul( time ) );
position = wrapper.wrapPosition( position );
PrototypeShip代码如下所示:
package javagames.prototype;
import java.awt.*;
import java.util.ArrayList;
import javagames.util.*;
public class PrototypeShip {
private float angle;
private float acceleration;
private float friction;
private float maxVelocity;
private float rotationDelta;
private float curAcc;
private Vector2f position;
private Vector2f velocity;
private PolygonWrapper wrapper;
private boolean damaged;
private Vector2f[] polyman;
private ArrayList<Vector2f[]> renderList;
public PrototypeShip( PolygonWrapper wrapper ) {
this.wrapper = wrapper;
friction = 0.25f;
rotationDelta = (float)Math.toRadians( 180.0 );
acceleration = 1.0f;
maxVelocity = 0.5f;
velocity = new Vector2f();
position = new Vector2f();
polyman = new Vector2f[] {
new Vector2f( 0.0325f, 0.0f ),
new Vector2f( -0.0325f, -0.0325f ),
new Vector2f( 0.0f, 0.0f ),
new Vector2f( -0.0325f, 0.0325f ),
};
renderList = new ArrayList<Vector2f[]>();
}
public void setDamaged( boolean damaged ) {
this.damaged = damaged;
}
public boolean isDamaged() {
return damaged;
}
public void rotateLeft( float delta ) {
angle += rotationDelta * delta;
}
public void rotateRight( float delta ) {
angle -= rotationDelta * delta;
}
public void setThrusting( boolean thrusting ) {
curAcc = thrusting ? acceleration : 0.0f;
}
public void setAngle( float angle ) {
this.angle = angle;
}
public PrototypeBullet launchBullet() {
Vector2f bulletPos = position.add( Vector2f.polar( angle, 0.0325f ) );
return new PrototypeBullet( bulletPos, angle );
}
public void update( float time ) {
updatePosition( time );
renderList.clear();
Vector2f[] world = transformPolygon();
renderList.add( world );
wrapper.wrapPolygon( world, renderList );
}
private Vector2f[] transformPolygon() {
Matrix3x3f mat = Matrix3x3f.rotate( angle );
mat = mat.mul( Matrix3x3f.translate( position ) );
return transform( polyman, mat );
}
private void updatePosition( float time ) {
Vector2f accel = Vector2f.polar( angle, curAcc );
velocity = velocity.add( accel.mul( time ) );
float maxSpeed = Math.min( maxVelocity / velocity.len(), 1.0f );
velocity = velocity.mul( maxSpeed );
float slowDown = 1.0f - friction * time;
velocity = velocity.mul( slowDown );
position = position.add( velocity.mul( time ) );
position = wrapper.wrapPosition( position );
}
private Vector2f[] transform( Vector2f[] poly, Matrix3x3f mat ) {
Vector2f[] copy = new Vector2f[ poly.length ];
for( int i = 0; i < poly.length; ++i ) {
copy[i] = mat.mul( poly[i] );
}
return copy;
}
public void draw( Graphics2D g, Matrix3x3f view ) {
g.setColor( new Color( 50, 50, 50 ) );
for( Vector2f[] poly : renderList ) {
for( int i = 0; i < poly.length; ++i ) {
poly[i] = view.mul( poly[i] );
}
g.setColor( Color.DARK_GRAY );
Utility.fillPolygon( g, poly );
g.setColor( isDamaged() ? Color.RED : Color.GREEN );
Utility.drawPolygon( g, poly );
}
}
public boolean isTouching( PrototypeAsteroid asteroid ) {
for( Vector2f[] poly : renderList ) {
for( Vector2f v : poly ) {
if( asteroid.contains( v ) ) {
return true;
}
}
}
return false;
}
}```
FlyingShipExample位于javagames.prototype包中,如图8.11所示,它使用飞船和子弹代码来测试飞船在屏幕上的来回飞行。向左箭头和向右箭头会旋转飞船,向上箭头使飞船向前移动,而空格键发射子弹。
<div style="text-align: center"><img src="https://yqfile.alicdn.com/7bceaa8fb0150027f1a3aa9d8a5f74f2e46f3043.png" width="" height="">
</div>
注意updateObject()方法中Java的魔力。如果在遍历子弹的集合时试图移除子弹,那么,该列表将会抛出一个ConcurrentModificationException。生成该列表的一个副本并且将其从最初的列表中移除,这样就不会抛出异常。
// FlyingShipExample.java
protected void updateObjects( float delta ) {
super.updateObjects( delta );
ship.update( delta );
ArrayList copy =
new ArrayList( bullets );
for( PrototypeBullet bullet : copy ) {
bullet.update( delta );
if( wrapper.hasLeftWorld( bullet.getPosition() ) ) {
bullets.remove( bullet );
}
}
}`
FlyingShipExample如下所示:
package javagames.prototype;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import javagames.util.*;
public class FlyingShipExample extends SimpleFramework {
private PrototypeShip ship;
private PolygonWrapper wrapper;
private ArrayList<PrototypeBullet> bullets;
public FlyingShipExample() {
appBorderScale = 0.9f;
appWidth = 640;
appHeight = 640;
appMaintainRatio = true;
appSleep = 1L;
appTitle = "Flying Ship Example";
}
@Override
protected void initialize() {
super.initialize();
bullets = new ArrayList<PrototypeBullet>();
wrapper = new PolygonWrapper( appWorldWidth, appWorldHeight );
ship = new PrototypeShip( wrapper );
}
@Override
protected void processInput( float delta ) {
super.processInput( delta );
if( keyboard.keyDown( KeyEvent.VK_LEFT ) ) {
ship.rotateLeft( delta );
}
if( keyboard.keyDown( KeyEvent.VK_RIGHT ) ) {
ship.rotateRight( delta );
}
if( keyboard.keyDownOnce( KeyEvent.VK_SPACE ) ) {
bullets.add( ship.launchBullet() );
}
ship.setThrusting( keyboard.keyDown( KeyEvent.VK_UP ) );
}
@Override
protected void updateObjects( float delta ) {
super.updateObjects( delta );
ship.update( delta );
ArrayList<PrototypeBullet> copy =
new ArrayList<PrototypeBullet>( bullets );
for( PrototypeBullet bullet : copy ) {
bullet.update( delta );
if( wrapper.hasLeftWorld( bullet.getPosition() ) ) {
bullets.remove( bullet );
}
}
}
@Override
protected void render( Graphics g ) {
super.render( g );
g.drawString( "Rotate: Left/Right Arrow", 20, 35 );
g.drawString( "Thrust: Up Arrow", 20, 50 );
g.drawString( "Fire: Space Bar", 20, 65 );
Matrix3x3f view = getViewportTransform();
ship.draw( (Graphics2D)g, view );
for( PrototypeBullet b : bullets ) {
b.draw( (Graphics2D)g, view );
}
}
public static void main( String[] args ) {
launchApp( new FlyingShipExample() );
}