本节书摘来异步社区《Java 2D游戏编程入门》一书中的第8章,第8.2节,作者:【美】Timothy Wright(莱特),更多章节内容可以访问云栖社区“异步社区”公众号查看。
PrototypeAsteroid类位于javagames.prototype包中,它表示一个穿越太空的陨石。在创建的时候,使用了一个随机的速率和旋转。Java的随机数生成器只能返回0到1之间的浮点数,因此,要创建在任意范围内分布的随机数,需要一些额外的步骤。例如,要返回-3到7之间的随机数,应按照如下步骤进行。
1.用最大值减去最小值,计算随机数之间的差距。2.生成从0到1的一个随机浮点数。3.将随机数乘以差距值。4.通过加上最小值来迁移范围。这些步骤听起来有些令人混淆,实际上并非如此。
private float getRandomFloat( float min, float max ) { float rand = new Random().nextFloat(); return rand * (max - min) + min; }``` 要得到一个随机的整数,也可以采用相同的步骤。由于随机数生成器返回范围从0到计数值减去1的一个数字,因此该方法需要略作调整。private float getRandomRadians( int minDegree, int maxDegree ) { int rand = new Random().nextInt( maxDegree - minDegree + 1 ); return (float)Math.toRadians( rand + minDegree );}`getRandomRotationDelta()方法返回了(5,45)到(–5,–45)之间的一个角度(弧度表示),如图8.8所示。
private float getRandomRotationDelta() { float radians = getRandomRadians( 5, 45 ); return new Random().nextBoolean()? radians : -radians; }``` 还可以使用setter和getter方法来访问小行星的某些属性。public void setPolygon( Vector2f[] polygon );public void setPosition( Vector2f position );public Vector2f getPosition();public void setSize( PrototypeAsteroid.Size size );public PrototypeAster.Size getSize()`大小是一个枚举类型的值:
public class PrototypeAsteroid { public enum Size { Large, Medium, Small; } //... }``` update()方法负责调整小行星的位置和旋转。注意,除了调整位置和旋转,PolygonWrapper类用来折返多边形的位置。 draw()方法负责用给定的视口矩阵和Graphics对象绘制多边形。要绘制填充的多边形,需要给Utility类添加两个方法:package javagames.util;import java.awt.*;import java.util.List;
public class Utility { // ... Other methods left out // ... New methods are below public static void fillPolygon( Graphics2D g, Vector2f[] polygon ) { Polygon p = new Polygon(); for( Vector2f v : polygon ) { p.addPoint( (int)v.x, (int)v.y ); } g.fill( p ); } public static void fillPolygon( Graphics2D g, List polygon ) { Polygon p = new Polygon(); for( Vector2f v : polygon ) { p.addPoint( (int)v.x, (int)v.y ); } g.fill( p ); }}`pointInPolygon()方法和前面所讨论的方法相同。contains()方法接受一个点,如果PolygonWrapper所复制的任何多边形包含该点的话,它返回true。注意,PrototypeAsteroid中没有实际的模型代码。那是编辑器和工厂的工作,我们将在后面介绍。
package javagames.prototype; import java.awt.*; import java.util.*; import javagames.util.*; public class PrototypeAsteroid { public enum Size { Large, Medium, Small; } private PolygonWrapper wrapper; private Size size; private float rotation; private float rotationDelta; private Vector2f[] polygon; private Vector2f position; private Vector2f velocity; private ArrayList<Vector2f[]> renderList; public PrototypeAsteroid( PolygonWrapper wrapper ) { this.wrapper = wrapper; renderList = new ArrayList<Vector2f[]>(); velocity = getRandomVelocity(); rotationDelta = getRandomRotationDelta(); } private Vector2f getRandomVelocity() { float angle = getRandomRadians( 0, 360 ); float radius = getRandomFloat( 0.06f, 0.3f ); return Vector2f.polar( angle, radius ); } private float getRandomRadians( int minDegree, int maxDegree ) { int rand = new Random().nextInt( maxDegree - minDegree + 1 ); return (float)Math.toRadians( rand + minDegree ); } private float getRandomRotationDelta() { float radians = getRandomRadians( 5, 45 ); return new Random().nextBoolean() ? radians : -radians; } private float getRandomFloat( float min, float max ) { float rand = new Random().nextFloat(); return rand * (max - min) + min; } public void setPolygon( Vector2f[] polygon ) { this.polygon = polygon; } public void setPosition( Vector2f position ) { this.position = position; } public Vector2f getPosition() { return position; } public void setSize( Size size ) { this.size = size; } public Size getSize() { return size; } public void update( float time ) { position = position.add( velocity.mul( time ) ); position = wrapper.wrapPosition( position ); rotation += rotationDelta * time; renderList.clear(); Vector2f[] world = transformPolygon(); renderList.add( world ); wrapper.wrapPolygon( world, renderList ); } private Vector2f[] transformPolygon() { Matrix3x3f mat = Matrix3x3f.rotate( rotation ); mat = mat.mul( Matrix3x3f.translate( position ) ); return transform( polygon, mat ); } 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 ) { for( Vector2f[] poly : renderList ) { for( int i = 0; i < poly.length; ++i ) { poly[i] = view.mul( poly[i] ); } g.setColor( Color.LIGHT_GRAY ); Utility.fillPolygon( g, poly ); g.setColor( Color.BLACK ); Utility.drawPolygon( g, poly ); } } public boolean contains( Vector2f point ) { for( Vector2f[] polygon : renderList ) { if( pointInPolygon( point, polygon ) ) { return true; } } return false; } private boolean pointInPolygon( Vector2f point, Vector2f[] polygon ) { boolean inside = false; Vector2f start = polygon[ polygon.length - 1 ]; boolean startAbove = start.y >= point.y; for( int i = 0; i < polygon.length; ++i ) { Vector2f end = polygon[i]; boolean endAbove = end.y >= point.y; if( startAbove != endAbove ) { float m = (end.y - start.y) / (end.x - start.x); float x = start.x + (point.y - start.y) / m; if( x >= point.x ) { inside = !inside; } } startAbove = endAbove; start = end; } return inside; }