Let Them Fight Wide
Mini Game Tutorial > Let Them Fight

This chapter is under construction. Thanks for your patience.

In this chapter, you will learn how to:

  • implement multiple states
  • change the current state

Wanted dead or alive

Now TankGame has all the characters - the Tank, CannonBalls, Enemies, and AimingSight. It launches. It explodes. So what do we need?

Actually we still need some stuff to make this game playable. There is no "game over." There is no score, no high score, and much more. Yes, they want to fight each other. Enemies want your tank dead or alive, so let them fight!

Let Them Fight

Let me explain a little bit about what we will create before we get started.

  • There are three tanks available
    • The game shows "Game Over" when no tank is left
  • The tank is dead when an enemy reaches the border line
  • An enemy is destroyed when it is blown by a cannonball
  • You can go to the next stage when all the tank are destroyed
    • The game shows the stage number at the beginning of each stage.
  • Enemies move a little faster at the next stage

Okay, here we go!

Change the rules - by updating the Model

Initialization routines

class TankGameModel
  MAX_LIFE = 3
  
  # invoked at the beginning of each stage
  def reset()
    @tank = Tank.new()
    @enemies = []
    @cannons = []
    @cleared = false
    @dead = false
    Enemy::MAX_ENEMIES.times do |i| 
      @enemies << Enemy.new(i, @speed)
    end
  end

  # invoked when this game is selected
  def init()
    @highScore = Preferences.valueForKey('HighScore').to_i
    @score = 0
    @stage = 1
    @life = MAX_LIFE
    @speed = 4
  end

Checking dead or alive

 def moveCharacters()
   reset() if (@dead || @cleared)
   @borderline = 200
   @cannons.each do |cannon|
     cannon.move()
     cannon.checkCollision(@enemies)
     @cannons.delete(cannon) if (!cannon.active)
   end

   @enemies.each do |enemy|
     enemy.move()
     if (!enemy.active)
       @enemies.delete(enemy)
       @score += 100
       @highScore = @score if (@score > @highScore)
     end
     die() if (enemy.reached?(@borderline))
   end
   clearStage() if (@enemies.size == 0)
 end

 def die()
   @life -= 1
   @dead = true
   @gameOver = true if (@life <= 0)
 end

 def clearStage()
   @cleared = true
   @speed += 1
   @stage += 1
 end

Recording High Scores

 def recordHighScore()
   Preferences.setValue('HighScore', @highScore)
 end

Let Them Begin

To show the stage number at the beginning of each stage.....

class TankGameStartState < State
  GAMESTART_TIMER = 1002
  def enter()
    super()
    @model.reset()
    @timer = TimerEvent.new(3.0, false, GAMESTART_TIMER)
  end

  def tick(id=nil)
    changeState(TankGamePlayingState)
    return true
  end
end

Let Them end the fight

class TankGameOverState < State
  GAMEOVER_TIMER = 1001
  def enter()
    super()
    @timer = TimerEvent.new(3.0, false, GAMEOVER_TIMER)
  end

  def tick(id=0)
    @model.recordHighScore()
    quit()
    return true
  end
end

Draw everything we need

Draw the score and available tanks

class TankGameView < View
 # updated
 def drawStatus()
   image = @model.tank.image
   x = @bounds.size.width - image.size.width / 4 - 15
   y = @bounds.size.height - image.size.height / 4 - 15
   @model.life.times do |i|
     destRect = NSRect.new(x, y, 
                           image.size.width / 4, image.size.height / 4)
     srcRect = NSRect.new([0,0], image.size)
     image.drawInRect(destRect, :fromRect, srcRect,:operation, 
                       NSCompositeSourceOver, :fraction, 1.0)
     x -= image.size.width / 4 + 5
   end

   x = 20 
   y = @bounds.size.height - 50
   drawText(sprintf('%05d', @model.score), 40, NSColor.blackColor, [x,y])
 end

  def drawRect()
    NSColor.whiteColor.colorWithAlphaComponent(0.9).set
    NSRectFill(@bounds)
    # Using shadow can be a performance bottleneck
    # setShadow(NSColor.lightGrayColor, [6.0, -6.0], 3.7, 0.6)
   
    NSColor.blackColor.set
    path = NSBezierPath.bezierPath
    point = NSPoint.new(0, @model.borderline)
    path.moveToPoint(point)
    point.x = Field.width
    path.lineToPoint(point)
    path.stroke

    drawStatus() # <------ updated

    @model.tank.draw()
    (@model.enemies + @model.cannons).each {|obj| obj.draw()}
    @model.tank.aiming.draw()
  end
end

View classes for new states

TankGameStartView

class TankGameStartView < TankGameView
  def drawRect()
    NSColor.whiteColor.colorWithAlphaComponent(0.9).set
    NSRectFill(@bounds)
#    setShadow(NSColor.lightGrayColor, [6.0, -6.0], 3.7, 0.6)
    drawCenterizedText("Stage #{@model.stage}", 100, NSColor.blackColor)
    drawStatus()
  end
end
Stage1.jpg SIZE:800x622(?KB)
Showing the stage number

TankGameOverState

class TankGameOverView < TankGameView
  def drawRect()
    NSColor.whiteColor.set
    NSRectFill(@bounds)
#    setShadow(NSColor.lightGrayColor, [6.0, -6.0], 3.7, 0.6)
    drawStatus()
    drawCenterizedText("GAME OVER", 100, NSColor.blackColor)
    score = @model.score
    highScore = @model.highScore
    if (score == highScore)
      drawCenterizedText("You made a high score", 50, NSColor.redColor, 
                       NSPoint.new(0, -200))
    else
      drawCenterizedText("High Score: #{highScore}", 50, NSColor.orangeColor, 
                       NSPoint.new(150, -200))
    end
  end
end
GameOver.jpg SIZE:800x622(?KB)
Game Over

Bind everything again

class TankGame < Game
  def initialize()
    # +-- updated
    # v
    @bindings = [[TankGamePlayingState, TankGameModel, TankGameView],
                [TankGameStartState, TankGameModel, TankGameStartView],
                [TankGameOverState, TankGameModel, TankGameOverView]]
    @initialState = TankGameStartState
  end

Check it out

LetThemFight.jpg SIZE:800x622(?KB)
Final Game View

What we've created so far

LetThemFight.tar.gz SIZE:x(?KB) is the file that we made so far. Here are the steps to run the game.

  1. Untar the tar.gz file at the project folder
  2. Add a group named "TankGame" to the "Resources" group at the left pane of the Xcode project window. (if not exists)
  3. Right click the group and select "Add" -> "Existing Files" and add all the tiff files (Add only TankGame.tiff if you already added other images)
  4. Run the application by pressing the "Build & Run" button

Let It Blow<Prev|Top:Mini Game Tutorial|Next>Let It Sound

Leave a comment

Begin the comment with //pukiwiki if you want to write a comment in PukiWiki format.

You must be logged in to post a comment.