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

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

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

8.7 编写原型游戏

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

20765ff4c59ca5899cb50dd0396c2754a4d31336

该原型游戏使用了我们在本章前面所介绍的如下的类。

  • PolygonWrapper
  • PrototypeShip
  • PrototypeAsteroid
  • PrototypeAsteroidFactory
  • PrototypeBullet

当你在本章末尾尝试编译和运行代码之前,确保已经创建了这些类。

initialize()方法为原型创建了所有这些对象,包括创建了一些星星作为背景。如下的代码创建了星星,并且创建了颜色的一个数组。Color类接受3个值:red、blue和green,这3个值在0到1之间。将每种颜色值设置为相同的值,将会产生灰色的阴影。本书稍后会详细介绍颜色,但现在,只要使用Java语言所提供的java.awt.Color类就行了。

// PrototypeGame.java
private void createStars() {
stars = new Vector2f[ STAR_COUNT ];
  colors = new Color[ STAR_COUNT ];
  for( int i = 0; i < stars.length; ++i ) { 
    float x = rand.nextFloat() * 2.0f - 1.0f;
    float y = rand.nextFloat() * 2.0f - 1.0f;
    stars[i] = new Vector2f( x, y );
    float color = rand.nextFloat();
    colors[i] = new Color( color, color, color );
  }
}```
当创建小行星的时候,注意getAsteroidStartPosition()方法。和产生各种大小的随机小行星的示例不同,这个方法只创建一个较大的随机小行星,并且使用如下代码将其放置到一个圆圈中,以使得它们不会在飞船之上产生。

// PrototypeGame.java
private Vector2f getAsteroidStartPosition() {
  float angle = (float)Math.toRadians( rand.nextInt( 360 ) );
  float minimum = appWorldWidth / 4.0f;
  float extra = rand.nextFloat() * minimum;
  float radius = minimum + extra;
  return Vector2f.polar( angle, radius );
}`
前面的代码在圆圈中放置了新的多边形,该圆圈是屏幕的四分之一大,如图8.13所示。

036c14119a901bbef04e11d5f25c08cd99410ba5

这会防止这种现象发生:新产生的小行星出现于飞船的顶部,玩家还没来得及开火,就碰到它并爆炸了。这是游戏程序员所面对的各种挑战的一个很好的例子。只有在原型游戏开始运行时,这个问题才会变得明显。不管游戏多么简单,总是会有奇怪的问题需要解决。

processInput()方法使用向左键和向右键来旋转飞船,向上键会激活加速动作,空格键会发射子弹,Escape键会重新产生小行星。

当子弹击中小行星时,不仅小行星会从渲染列表中删除,而且如果小行星不是太小的话,它会分裂成两块更小的小行星。

updateShip()方法检查碰撞。如果飞船被击中,会设置毁灭标志。尽管在真实的游戏中,当飞船被击中时,游戏会重新启动,但我们还没有介绍玩家生命或游戏结束状态的概念,因此,目前当飞船被击中时,将其绘制为红色。

render()方法绘制星星、所有的小行星、子弹、飞船以及常用的帧速率和指令。还有一些新的代码,它们会开启抗锯齿功能,以使线条绘制得更为平滑。第10章将会介绍抗锯齿。

Graphics2D g2d = (Graphics2D)g;
    g2d.setRenderingHint(
      RenderingHints.KEY_ANTIALIASING,
      RenderingHints.VALUE_ANTIALIAS_ON
    );```
drawStars()、drawShip()、drawAsteroids()和drawBullets()方法负责绘制原型中的各种物体。PrototypeGame的代码如下所示:

package javagames.prototype;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.util.*;
import javagames.prototype.PrototypeAsteroid.Size;
import javagames.util.*;

public class PrototypeGame extends SimpleFramework {
  private static final int STAR_COUNT = 1500;
  private PrototypeShip ship;
  private PolygonWrapper wrapper;
  private PrototypeAsteroidFactory factory;
  private ArrayList bullets;
  private ArrayList asteroids;
  private Random rand;
  private Vector2f[] stars;
  private Color[] colors;
  public PrototypeGame() {
    appBorderScale = 0.9f;
    appWidth = 640;
    appHeight = 640;
    appMaintainRatio = true;
    appSleep = 1L;
    appTitle = "Prototype Game";
  }
  @Override
  protected void initialize() {
    super.initialize();
    // create game objects
    rand = new Random();
    bullets = new ArrayList();
    asteroids = new ArrayList();
    wrapper = new PolygonWrapper( appWorldWidth, appWorldHeight );
    ship = new PrototypeShip( wrapper );
    factory = new PrototypeAsteroidFactory( wrapper );
    createStars();
    createAsteroids();
  }
  // this creates the random stars for the background
  private void createStars() {
    stars = new Vector2f[ STAR_COUNT ];
    colors = new Color[ STAR_COUNT ];
    for( int i = 0; i < stars.length; ++i ) {
      float x = rand.nextFloat() * 2.0f - 1.0f;
      float y = rand.nextFloat() * 2.0f - 1.0f;
      stars[i] = new Vector2f( x, y );
      float color = rand.nextFloat();
      colors[i] = new Color( color, color, color );
    }
  }
  // create the random asteroids
  private void createAsteroids() {
    asteroids.clear();
    for( int i = 0; i < 4; ++i ) {
      Vector2f position = getAsteroidStartPosition();
      asteroids.add( factory.createLargeAsteroid( position ) );
    }
  }
  // create random position for an asteroid
  private Vector2f getAsteroidStartPosition() {
    float angle = (float)Math.toRadians( rand.nextInt( 360 ) );
    float minimum = appWorldWidth / 4.0f;
    float extra = rand.nextFloat() * minimum;
    float radius = minimum + extra;
    return Vector2f.polar( angle, radius );
  }
  @Override
  protected void processInput( float delta ) {
    super.processInput( delta );
    // fly the ship
    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() );
    }
    if( keyboard.keyDownOnce( KeyEvent.VK_ESCAPE ) ) {
      createAsteroids();
    }
    ship.setThrusting( keyboard.keyDown( KeyEvent.VK_UP ) );
  }
  @Override
  protected void updateObjects( float delta ) {
    super.updateObjects( delta );
    updateAsteroids( delta );
    updateBullets( delta );
    updateShip( delta );
  }
  private void updateAsteroids( float delta ) {
    for( PrototypeAsteroid asteroid : asteroids ) {
      asteroid.update( delta );
    }
  }
  private void updateBullets( float delta ) {
    ArrayList copy =
      new ArrayList( bullets );
    for( PrototypeBullet bullet : copy ) {
      updateBullet( delta, bullet );
    }
  }
  // check for bullet collisions
  private void updateBullet( float delta, PrototypeBullet bullet ) {
    bullet.update( delta );
    if( wrapper.hasLeftWorld( bullet.getPosition() ) ) {
      bullets.remove( bullet );
    } else {
      ArrayList ast =
        new ArrayList( asteroids );
      for( PrototypeAsteroid asteroid : ast ) {
        if( asteroid.contains( bullet.getPosition() ) ) {
          bullets.remove( bullet );
          asteroids.remove( asteroid );
          spawnBabies( asteroid );
        }
      }
    }
  }
  // create smaller asteroids when one is broken apart
  private void spawnBabies( PrototypeAsteroid asteroid ) {
    if( asteroid.getSize() == Size.Large ) {
      asteroids.add(
        factory.createMediumAsteroid( asteroid.getPosition() ) );
      asteroids.add(
        factory.createMediumAsteroid( asteroid.getPosition() ) );
    }
    if( asteroid.getSize() == Size.Medium ) {
      asteroids.add(
        factory.createSmallAsteroid( asteroid.getPosition() ) );
      asteroids.add(
        factory.createSmallAsteroid( asteroid.getPosition() ) );
    }
  }
  // update the ship object
  private void updateShip( float delta ) {
    ship.update( delta );
    boolean isHit = false;
    for( PrototypeAsteroid asteroid : asteroids ) {
      if( ship.isTouching( asteroid ) ) {
        isHit = true;
      }
    }
    ship.setDamaged( isHit );
  }
  @Override
  protected void render( Graphics g ) {
    // render instructions
    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 );
    g.drawString( "Press ESC to respawn", 20, 80 );
    Graphics2D g2d = (Graphics2D)g;
    g2d.setRenderingHint(
      RenderingHints.KEY_ANTIALIASING,
      RenderingHints.VALUE_ANTIALIAS_ON
    );
    // draw game objects
    Matrix3x3f view = getViewportTransform();
    drawStars( g2d, view );
    drawAsteroids( g2d, view );
    drawBullets( g2d, view );
    drawShip( g2d, view );
  }
  private void drawStars( Graphics2D g, Matrix3x3f view ) {
    for( int i = 0; i < stars.length; ++i ) {
      g.setColor( colors[i] );
      Vector2f screen = view.mul( stars[i] );
      g.fillRect( (int)screen.x, (int)screen.y, 1, 1 );
    }
  }
  private void drawShip( Graphics2D g, Matrix3x3f view ) {
    ship.draw( g, view );
  }
  private void drawAsteroids( Graphics2D g, Matrix3x3f view ) {
    for( PrototypeAsteroid asteroid : asteroids ) {
      asteroid.draw( g, view );
    }
  }
  private void drawBullets( Graphics2D g, Matrix3x3f view ) {
    for( PrototypeBullet b : bullets ) {
      b.draw( g, view );
    }
  }
  public static void main( String[] args ) {
    launchApp( new PrototypeGame() );
  }
}`

相关文章
|
17天前
|
Java
Java猜数字游戏
Java猜数字游戏
16 2
|
30天前
|
Java Android开发
基于Java的坦克大战游戏的设计与实现
基于Java的坦克大战游戏的设计与实现
13 0
|
20天前
|
关系型数据库 Java 开发工具
Java入门高频考查基础知识9(15问万字参考答案)
本文探讨了Spring Cloud的工作原理,包括注册中心的心跳机制、服务发现机制,以及Eureka默认的负载均衡策略。同时,概述了Spring Boot中常用的注解及其实现方式,并深入讨论了Spring事务的注解、回滚条件、传播性和隔离级别。文章还介绍了MySQL的存储引擎及其区别,特别关注了InnoDB如何实现MySQL的事务处理。此外,本文还详细探讨了MySQL索引,包括B+树的原理和设计索引的方法。最后,比较了Git和SVN的区别,并介绍了Git命令的底层原理及流程。
29 0
Java入门高频考查基础知识9(15问万字参考答案)
|
20天前
|
存储 缓存 算法
Java入门高频考查基础知识4(字节跳动面试题18题2.5万字参考答案)
最重要的是保持自信和冷静。提前准备,并对自己的知识和经验有自信,这样您就能在面试中展现出最佳的表现。祝您面试顺利!Java 是一种广泛使用的面向对象编程语言,在软件开发领域有着重要的地位。Java 提供了丰富的库和强大的特性,适用于多种应用场景,包括企业应用、移动应用、嵌入式系统等。下是几个面试技巧:复习核心概念、熟悉常见问题、编码实践、项目经验准备、注意优缺点、积极参与互动、准备好问题问对方和知其所以然等,多准备最好轻松能举一反三。
46 0
Java入门高频考查基础知识4(字节跳动面试题18题2.5万字参考答案)
|
20天前
|
存储 算法 JavaScript
Java入门高频考查算法逻辑基础知识3-编程篇(超详细18题1.8万字参考编程实现)
解决这类问题时,建议采取下面的步骤: 理解数学原理:确保你懂得基本的数学公式和法则,这对于制定解决方案至关重要。 优化算法:了解时间复杂度和空间复杂度,并寻找优化的机会。特别注意避免不必要的重复计算。 代码实践:多编写实践代码,并确保你的代码是高效、清晰且稳健的。 错误检查和测试:要为你的代码编写测试案例,测试标准的、边缘情况以及异常输入。 进行复杂问题简化:面对复杂的问题时,先尝试简化问题,然后逐步分析和解决。 沟通和解释:在编写代码的时候清晰地沟通你的思路,不仅要写出正确的代码,还要能向面试官解释你的
32 0
|
20天前
|
存储 Java 编译器
Java入门高频考查基础知识2(超详细28题2.5万字答案)
多态是面向对象编程中的一个重要概念,它允许不同类的对象对同一消息作出不同的响应。在具体实现上,多态允许一个父类的引用指向其子类的对象,并根据实际指向的对象的类型来调用相应的方法。在 Java 中,多态可以通过以下几种方式实现:在同一个类中,方法名相同,但形参列表不同,实现了多态。子类可以重写(覆盖)其父类的方法,实现多态。在父类引用中调用该方法时,根据实际指向的子类对象的类型来调用相应的方法实现。
38 0
|
21天前
|
编解码 算法 安全
【Java技术专题】「入门到精通系列」深入探索Java技术中常用到的六种加密技术和实现
【Java技术专题】「入门到精通系列」深入探索Java技术中常用到的六种加密技术和实现
44 0
|
1月前
|
算法 Java
Java必刷入门递归题×5(内附详细递归解析图)
Java必刷入门递归题×5(内附详细递归解析图)
21 1
|
1天前
|
安全 Java
java多线程(一)(火车售票)
java多线程(一)(火车售票)
|
1天前
|
安全 Java 调度
Java并发编程:深入理解线程与锁
【4月更文挑战第18天】本文探讨了Java中的线程和锁机制,包括线程的创建(通过Thread类、Runnable接口或Callable/Future)及其生命周期。Java提供多种锁机制,如`synchronized`关键字、ReentrantLock和ReadWriteLock,以确保并发访问共享资源的安全。此外,文章还介绍了高级并发工具,如Semaphore(控制并发线程数)、CountDownLatch(线程间等待)和CyclicBarrier(同步多个线程)。掌握这些知识对于编写高效、正确的并发程序至关重要。