天天看點

《Java 2D遊戲程式設計入門》—— 8.1 建立一個多邊形包裝類

本節書摘來異步社群《java 2d遊戲程式設計入門》一書中的第8章,第8.1節,作者:【美】timothy wright(萊特),更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

制作一款2d太空飛船遊戲時,首先要解決的大問題是,當飛船到達螢幕邊緣時,會發生什麼事情。一個解決方案是,将飛船保持在螢幕的中央,而移動其周圍的環境。沒有對加載檔案的任何支援,也沒有關卡編輯器的話,要做到這點似乎有點難。另一個選擇是,當飛船到達螢幕邊界的時候,将其彈回。這樣做似乎顯得很奇怪。第三種選擇是,讓飛船從一端到另一端折返。讓我們采用第三種方法。

還有很多方法可以解決這一問題。一種方法是,讓飛船完全離開螢幕,然後讓其在另一端傳回,如圖8.1所示。

嘗試這一想法似乎很簡單。我們使用圍繞飛船的一個邊界框來測試飛船何時離開了螢幕,并且将其移動到另外一端。這種方法有兩個問題:一個問題是,可能會使得飛船在飛行中變成隻有很小的一塊可見,進而很難碰到它;第二個問題是,飛船會持續地處在螢幕之外。這并不經常發生,但是,當多邊形的大部分都在螢幕之外的時候,可能很難撞擊到該物體。由于存在這些問題,是以與一直等待直到整個物體離開螢幕相比,折返物體似乎是更好的選擇。

《Java 2D遊戲程式設計入門》—— 8.1 建立一個多邊形包裝類

折返物體以使得離開螢幕一端的任何部分從螢幕的另一端傳回,也需要擔心風險。當你将一個物體從一端折返到另一端的時候,同一物體可能會有4個副本,如圖8.2所示。

《Java 2D遊戲程式設計入門》—— 8.1 建立一個多邊形包裝類

這使得不僅需要繪制4個副本,而且所有4個副本都需要進行碰撞檢查。如果隻對第一個物體進行碰撞檢查,位于其對角的兩個物體可能就不會記錄碰撞,即便它們的繪制物體有重疊,如圖8.3所示。

建立一個渲染清單,其中包含了每個物體的副本,這可以解決該問題。物體可以繪制,并且整個清單可用來檢查碰撞。物體折返的時候不會遇到問題,因為一旦物體的中心到了遊戲世界的邊界之外,就會通過遊戲世界的寬度和高度來調整其位置以将其折返。應該總是将折返位置放置在邊界之中,這樣做物體就不會跑到螢幕之外,如圖8.4所示。

《Java 2D遊戲程式設計入門》—— 8.1 建立一個多邊形包裝類

// polygonwrapper.java

private vector2f getmin( vector2f[] poly ) {

  vector2f min = new vector2f( float.max_value, float.max_value );

  for( vector2f v : poly ) {

    min.x = math.min( v.x, min.x );

    min.y = math.min( v.y, min.y );

  }

  return min;

}

private vector2f getmax( vector2f[] poly ) {

  vector2f max = new vector2f( -float.max_value, -float.max_value );

    max.x = math.max( v.x, max.x );

    max.y = math.max( v.y, max.y );

  return max;

}<code>`</code>

wrappolygon()方法計算最小和最大值,并且使用這些值來判斷物體是否需要向北、南、東或西折返。如果物體向兩個主要方向折返,例如,向南和向東,那麼,它也需要向北和向西折返。四個主要方向的每一個都要進行測試。使用變換方法,複制每個折返多邊形,并且将其添加到渲染清單:

package javagames.prototype;

import java.util.list;

import javagames.util.matrix3x3f;

import javagames.util.vector2f;

public class polygonwrapper {

  private float worldwidth;

  private float worldheight;

  private vector2f worldmin;

  private vector2f worldmax;

  public polygonwrapper( float worldwidth, float worldheight ) {

    this.worldwidth = worldwidth;

    this.worldheight = worldheight;

    worldmax = new vector2f( worldwidth / 2.0f, worldheight / 2.0f );

    worldmin = worldmax.inv();

  public boolean hasleftworld( vector2f position ) {

    return position.x &lt; worldmin.x || position.x &gt; worldmax.x ||

      position.y &lt; worldmin.y || position.y &gt; worldmax.y;

  public vector2f wrapposition( vector2f position ) {

    vector2f wrapped = new vector2f( position );

    if( position.x &lt; worldmin.x ) {

      wrapped.x = position.x + worldwidth;

    } else if( position.x &gt; worldmax.x ) {

      wrapped.x = position.x - worldwidth;

    }

    if( position.y &lt; worldmin.y ) {

      wrapped.y = position.y + worldheight;

    } else if( position.y &gt; worldmax.y ) {

      wrapped.y = position.y - worldheight;

    return wrapped;

  public void wrappolygon( vector2f[] poly, list renderlist ) {

    vector2f min = getmin( poly );

    vector2f max = getmax( poly );

    boolean north = max.y &gt; worldmax.y;

    boolean south = min.y &lt; worldmin.y;

    boolean west = min.x &lt; worldmin.x;

    boolean east = max.x &gt; worldmax.x;

    if( west ) renderlist.add( wrapeast( poly ) );

    if( east ) renderlist.add( wrapwest( poly ) );

    if( north ) renderlist.add( wrapsouth( poly ) );

    if( south ) renderlist.add( wrapnorth( poly ) );

    if( north &amp;&amp; west ) renderlist.add( wrapsoutheast( poly ) );

    if( north &amp;&amp; east ) renderlist.add( wrapsouthwest( poly ) );

    if( south &amp;&amp; west ) renderlist.add( wrapnortheast( poly ) );

    if( south &amp;&amp; east ) renderlist.add( wrapnorthwest( poly ) );

  private vector2f getmin( vector2f[] poly ) {

    vector2f min = new vector2f( float.max_value, float.max_value );

    for( vector2f v : poly ) {

      min.x = math.min( v.x, min.x );

      min.y = math.min( v.y, min.y );

    return min;

  private vector2f getmax( vector2f[] poly ) {

    vector2f max = new vector2f( -float.max_value, -float.max_value );

      max.x = math.max( v.x, max.x );

      max.y = math.max( v.y, max.y );

    return max;

  private vector2f[] wrapnorth( vector2f[] poly ) {

    return transform( poly, matrix3x3f.translate( 0.0f, worldheight ) );

  private vector2f[] wrapsouth( vector2f[] poly ) {

    return transform( poly, matrix3x3f.translate( 0.0f, -worldheight ) );

  private vector2f[] wrapeast( vector2f[] poly ) {

    return transform( poly, matrix3x3f.translate( worldwidth, 0.0f ) );

  private vector2f[] wrapwest( vector2f[] poly ) {

    return transform( poly, matrix3x3f.translate( -worldwidth, 0.0f ) );

  private vector2f[] wrapnorthwest( vector2f[] poly ) {

    return transform(

      poly, matrix3x3f.translate( -worldwidth, worldheight )

    );

  private vector2f[] wrapnortheast( vector2f[] poly ) {

      poly, matrix3x3f.translate( worldwidth, worldheight )

  private vector2f[] wrapsoutheast( vector2f[] poly ) {

      poly, matrix3x3f.translate( worldwidth, -worldheight )

  private vector2f[] wrapsouthwest( vector2f[] poly ) {

      poly, matrix3x3f.translate( -worldwidth, -worldheight )

  private vector2f[] transform( vector2f[] poly, matrix3x3f mat ) {

    vector2f[] copy = new vector2f[ poly.length ];

    for( int i = 0; i &lt; poly.length; ++i ) {

      copy[i] = mat.mul( poly[i] );

    return copy;

位于javagames.prototype包中的screenwrapexample,如圖8.7所示,使用polygonwrapper類來折返一個螢幕上來回移動的方塊。pos向量儲存了方塊的位置。poly數組儲存了方塊多邊形。renderlist儲存了要折返的方塊的副本,以及最初的變換多邊形。折返變量是前面所介紹的polygonwrapper。

《Java 2D遊戲程式設計入門》—— 8.1 建立一個多邊形包裝類

initialize()方法建立了所有的物體并且将滑鼠設定為相對移動,進而不需要滑鼠離開螢幕物體就可以在螢幕上折返。如果使用絕對位置的話,processinput()方法使用滑鼠位置來移動方塊;如果使用相對移動的話,它通過将目前位置和相對移動相加來移動方塊。該方法還使用空格鍵來切換相對滑鼠移動。

transform()方法建立了對象的一個副本,并且通過給定的矩形來變換它。updateobjects()方法清理了渲染清單,将方塊折返并變換,然後使用polygonwrapper将物體的任何副本添加到渲染清單中。render()方法将渲染清單中的所有物體繪制到螢幕上。