Jump to content
  • Advertisement
Otay

Realm of the Tempest RPG

Recommended Posts

I'll share some technical notes about the transition effects since they were so fun.

Background Info

Three years ago I actually had a playable game.  Then I transferred to a university, and dropped my game dev hobby when my obsession with math and computer science sparked up instead.  When I returned to game dev earlier this year, I was disgusted by my code, especially its lack of encapsulation.  After considering my options for how to clean up my code, I painfully decided that rewriting everything from scratch was the lesser headache.

This game was started in 2011 with the UDK engine, and I have refused to switch due to how much I enjoy the aesthetic I worked so hard to achieve with all the lighting and materials.  UDK is not at all friendly with UI, so I write a lot of UI code from scratch.  After months of my eyes bleeding through UI code, programming these transition effects has been a much needed change of pace.

Transition Effects

UDK (kind of?) allows fancy shader code, I think?  Seems like most shader stuff is done in the editor interface rather than in code, which makes me sad.  I chose to implement my transition effects with a big array of black sprite tiles instead.

The premise of the algorithm for transition effects that I outlined before coding is this:

  1. Add all tiles to a dynamic array, initialize visibility on (or off)
  2. Sort the array based on each tiles distance to an equation of your choice
  3. With each tick from a timer, randomly select "k" of the first "m" elements out the total array of "n" tiles
  4. Change the visibility of the selected tiles, and remove them from the list
  5. Repeat until list is empty

All I needed to accomplish this was a static array of tiles, dynamic array of tile indices and distance calculations, a custom comparitor for sorting tiles by distance, and a dynamic array of function pointers to delegate sorting equations for the various effects.  I use the fancy delegate array so that I can customize what set of effects will be randomly picked from (notice fading into battle is always the vertical down one.)

I'm more of a C/C++ kind of guy, but I dont mind working in UDK's unrealscript language.  Here's the code.

  Data

// Tile count for transition effect
// 60x60 tiles, 1440x900 resolution, 1440/60 * 900/60
const TILE_COUNT = 360; 

const TILES_PER_ROW = 24;
const COLUMN_PER_ROW = 15;

// Tile sorting information
struct TileInfo {
  var int index;
  var int distance;
};

var private array<TileInfo> tileIndices;

// Sprite tiles
var private GUISprite blackSquares[TILE_COUNT];
  
// Effect functions
var private array<delegate<tileSorter> > sorterEffects;
delegate int tileSorter(int index);

Algorithm

/*============================================================================= 
 * tileSort()
 *
 * Sorts tiles for transition effect
 *===========================================================================*/
private function tileSort(delegate<tileSorter> sorter) {
  local TileInfo newTile;
  local int i;
  
  // Reset array
  tileIndices.length = 0;
  
  // Populate list
  for (i = 0; i < TILE_COUNT; i++) {
    newTile.index = i;
    newTile.distance = sorter(i);
    tileIndices.addItem(newTile);
  }
  
  tileIndices.sort(tileComparison);
}

/*============================================================================= 
 * tileComparison()
 *
 * Compares distance between tiles.  Negative result signifies out of order.
 *===========================================================================*/
private function int tileComparison(TileInfo a, TileInfo b) {
  if (a.distance < b.distance) {
    return -1;
  } else if (a.distance > b.distance) {
    return 1;
  } else {
    return 0;
  }
}

/*============================================================================= 
 * renderTiles()
 *
 * Timer allocated function for drawing tiles effects to the screen
 *===========================================================================*/
private function renderTiles() {
  local bool displayState;
  local int index, k;
  
  // Transition direction
  displayState = (transitionStyle == TRANSITION_OUT);
  
  for (k = 0; k < tilesPerTick; k++) {
    // Check for remaining indices
    if (tileIndices.length == 0) {
      endTransition();
      return;
    }
    
    // Render a random batch from the sorted list
    index = rand(20);
    if (index >= tileIndices.length) index = rand(tileIndices.length);
    blackSquares[tileIndices[index].index].setEnabled(displayState);
    tileIndices.remove(index, 1);
  }
}

Effect Equations


/*============================================================================= 
 * sortMethod1()
 *
 * Circle effect
 *===========================================================================*/
private function int sortMethod1(int index) {
  // Distance to x=1/2, y=1/2
  return distanceFormula(
    blackSquares[index].posX, 
    blackSquares[index].posy, 
    NATIVE_WIDTH / 2,
    NATIVE_HEIGHT / 2
  );
}

/*============================================================================= 
 * sortMethod2()
 *
 * Four corners effect
 *===========================================================================*/
private function int sortMethod2(int index) {
  local float d1, d2, d3, d4;
  
  // Distance to MIN(four corners)
  d1 = distanceFormula(
    blackSquares[index].posX, 
    blackSquares[index].posy, 
    0,
    0
  );
  
  d2 = distanceFormula(
    blackSquares[index].posX, 
    blackSquares[index].posy, 
    NATIVE_WIDTH,
    0
  );
  
  d3 = distanceFormula(
    blackSquares[index].posX, 
    blackSquares[index].posy, 
    0,
    NATIVE_HEIGHT
  );
  
  d4 = distanceFormula(
    blackSquares[index].posX, 
    blackSquares[index].posy, 
    NATIVE_WIDTH,
    NATIVE_HEIGHT
  );
  
  d1 = fMin(d1, d2);
  d2 = fMin(d3, d4);
  
  return fMin(d1, d2);
}

/*============================================================================= 
 * sortMethod3()
 *
 * Vertical lines effect
 *===========================================================================*/
private function int sortMethod3(int index) {
  local float d1, d2;
  
  // Distance to MIN(x=1/3, x=2/3)
  d1 = abs(blackSquares[index].posX - (NATIVE_WIDTH / 4));
  d2 = abs(blackSquares[index].posX - (3 * NATIVE_WIDTH / 4));
  return fMin(d1, d2);
}

/*============================================================================= 
 * sortMethod4()
 *
 * Horizontal line effect
 *===========================================================================*/
private function int sortMethod4(int index) {
  // Distance to Y = 1/2
  return abs(blackSquares[index].posY - (NATIVE_HEIGHT / 2));
}

/*============================================================================= 
 * sortMethod5()
 *
 * Randomized effect
 *===========================================================================*/
private function int sortMethod5(int index) {
  // Random method
  return rand(10);
}

/*============================================================================= 
 * sortMethod6()
 *
 * Vertical curtain effect, used only for opening combat
 *===========================================================================*/
private function int sortMethod6(int index) {
  // Distance to Y = 1
  return abs(blackSquares[index].posY - NATIVE_HEIGHT);
}

 

Final thoughts

Even though the mixing of 2D / 3D is the most frequently criticized design choice in my work, I'm personally happy with how this is turning out aesthetically.

 

Edited by Otay

Share this post


Link to post
Share on other sites
Advertisement

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!