本节书摘来异步社区《Java 2D游戏编程入门》一书中的第8章,第8.6节,作者:【美】Timothy Wright(莱特),更多章节内容可以访问云栖社区“异步社区”公众号查看。
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.javaprotected 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() ); }