《Java 2D游戏编程入门》—— 8.6 原型Ship类

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() );
  }
时间: 2024-10-05 14:51:19

《Java 2D游戏编程入门》—— 8.6 原型Ship类的相关文章

《Java 2D游戏编程入门》—— 导读

前言 多年前,当我第一次将软件开发作为专业工作的时候,有人请我编写一个applet.那时候,我对于Java语言知道得并不多.在整个上学期间,我很广泛地使用C++.我确实用Java编写过一些代码,但认为它太慢并且是C++的没落版. 同时,我购买和阅读了很多可以接触到的游戏编程图书.我通读了一本关于人工智能的书,其中包含很多不错的示例,但它们都是用C++和DirectX编写的.由于忙着学习Java以便在工作中使用,我认为将示例转换为由Java编写可能是学习这门语言的一种好办法. 毕竟,Java游戏编

《Java 2D游戏编程入门》—— 8.5 原型Bullet类

8.5 原型Bullet类 PrototypeBullet代码位于javagames.prototype包中,这是一个最简单的原型游戏源代码.除了绘制圆以便可以调整屏幕大小外,没有其他任何值得讨论的内容. package javagames.prototype; import java.awt.*; import javagames.util.*; public class PrototypeBullet { private Vector2f velocity; private Vector2f

《Java 2D游戏编程入门》—— 8.7 编写原型游戏

8.7 编写原型游戏 原型游戏如图8.12所示,位于javagames.prototype包中,它使用了我们目前为止所见过的所有技术.尽管这只是一个原型,并且目前还没有成为一款完整的游戏,但我已经展示了足够的工具来让一些功能奏效.如果要等到最后再制作一款游戏,可能需要等太长的时间. 该原型游戏使用了我们在本章前面所介绍的如下的类. PolygonWrapper PrototypeShip PrototypeAsteroid PrototypeAsteroidFactory PrototypeBu

《Java 2D游戏编程入门》—— 第8章 游戏原型

第8章 游戏原型 本章基于你在前面各章中已经学到的所有内容,介绍如何创建一个原型太空游戏.尽管它还有很多元素缺失,因而不能称之为一款完整的游戏,但我们已经有了足够的可用工具来创建一个可以工作的原型. 在初次学习编程的时候,我遇到的一个问题是,有众多的示例使用各种编程概念,如循环.变量.集成和多态,但是,并没有太多的示例真正做某件事情.这些示例要么太简单了,只是无用的代码片段,要么是极其复杂.编写糟糕的程序,其中还掺入了所有的计算机科学理论,而不是只用到完成任务所需的那些知识.本章的这款原型游戏并

《Java 2D游戏编程入门》—— 8.2 创建一个原型小行星

8.2 创建一个原型小行星 PrototypeAsteroid类位于javagames.prototype包中,它表示一个穿越太空的陨石.在创建的时候,使用了一个随机的速率和旋转.Java的随机数生成器只能返回0到1之间的浮点数,因此,要创建在任意范围内分布的随机数,需要一些额外的步骤.例如,要返回-3到7之间的随机数,应按照如下步骤进行. 1.用最大值减去最小值,计算随机数之间的差距. 2.生成从0到1的一个随机浮点数. 3.将随机数乘以差距值. 4.通过加上最小值来迁移范围. 这些步骤听起来

《Java 2D游戏编程入门》—— 8.3 创建一个原型编辑器

8.3 创建一个原型编辑器 现在是时候创建一些多边形了.尽管有可能可以猜到每个点,但要创建9个小行星形状,手动进行的话,工作量还是很大的.就像大多数程序一样,我也很懒.编写一个编辑器来创建多边形,这样会容易很多.位于javagames.prototype包中的PrototypeEditor,如图8.9所示. 如果模型存储为文件,它们将在运行时加载.然而,做这些事情所需的代码还没有介绍过,因此,编辑器将会作弊.当按下空格键的时候,编辑器会产生出能够粘贴到其他源文件中的代码.本书的第二部分将会介绍使

《Java 2D游戏编程入门》—— 8.4 用原型小行星工厂生产小行星

8.4 用原型小行星工厂生产小行星 位于javagames.prototype包中的PrototypeAsteroidFactory,包含了3个大的.3个中型的和3个小的小行星,它们都是使用编辑器并粘贴到代码中设计的.给定一个位置,这3个方法生成一个随机数来选择所返回的小行星: createLargeAsteroid( Vector2f position ) createMediumAsteroid( Vector2f position ) createSmallAsteroid( Vector

《Java 2D游戏编程入门》—— 2.4 相对鼠标移动

2.4 相对鼠标移动 对于图2.3所示的当前的鼠标输入类来说,有一个问题.首先,它看上去似乎挺明显,但是,只有在鼠标位于窗口之中的时候,程序才接受鼠标事件.一旦鼠标离开了窗口,鼠标指针的坐标位置在应用程序中就变得不再有效.更为糟糕的是,在全屏模式中,当鼠标到达屏幕边缘的时候,它会直接停下来,而不会继续注册事件.根据应用程序的需要,可能需要相对鼠标移动.好在,更新鼠标输入类以支持相对鼠标移动并不难. RelativeMouseInput类位于javagames.util包中,构建于前面示例中的类的

《Java 2D游戏编程入门》—— 8.1 创建一个多边形包装类

8.1 创建一个多边形包装类 制作一款2D太空飞船游戏时,首先要解决的大问题是,当飞船到达屏幕边缘时,会发生什么事情.一个解决方案是,将飞船保持在屏幕的中央,而移动其周围的环境.没有对加载文件的任何支持,也没有关卡编辑器的话,要做到这点似乎有点难.另一个选择是,当飞船到达屏幕边界的时候,将其弹回.这样做似乎显得很奇怪.第三种选择是,让飞船从一端到另一端折返.让我们采用第三种方法. 还有很多方法可以解决这一问题.一种方法是,让飞船完全离开屏幕,然后让其在另一端返回,如图8.1所示. 尝试这一想法似