Implementing Heads Up Display - Developing Games With Ruby (2014)

Developing Games With Ruby (2014)

Implementing Heads Up Display

In order to know what’s happening, we need some sort of HUD. We already have crosshair and radar, but they are scattered around in code. Now we want to display active powerup modifiers, so you would know what is your fire rate and speed, and if it’s worth getting one more powerup before going into the next fight.

Design Considerations

While creating our HUD class, we will have to start building game stats, because we want to display number of kills our tank made. Stats topic will be covered in depth later, but for now let’s assume that @tank.input.stats.killsgives us the kill count, which we want to draw in top-left corner of the screen, along with player health and modifier values.

HUD will also be responsible for drawing crosshair and radar.

Rendering Text With Custom Font

Previously, all text were rendered with Gosu.default_font_name, and we want something more fancy and more thematic, probably a dirty stencil based font like this one:

Armalite Rifle font

Armalite Rifle font

And one more fancy font will make our game title look good. Too bad we don’t have a title yet, but “Tanks Prototype” writen in a thematic way still looks pretty good.

To have convenient access to these fonts, we will add a helper methods in Utils:

module Utils

# ...

def self.title_font

media_path('top_secret.ttf')

end

def self.main_font

media_path('armalite_rifle.ttf')

end

# ...

end

Use it instead of Gosu.default_font_name:

size = 20

Gosu::Image.from_text($window, "Your text", Utils.main_font, size)

Implementing HUD Class

After we have put everything together, we will get HUD class:

12-stats/entities/hud.rb


1 class HUD

2 attr_accessor :active

3 def initialize(object_pool, tank)

4 @object_pool = object_pool

5 @tank = tank

6 @radar = Radar.new(@object_pool, tank)

7 end

8

9 def player=(tank)

10 @tank = tank

11 @radar.target = tank

12 end

13

14 def update

15 @radar.update

16 end

17

18 def health_image

19 if @health.nil? || @tank.health.health != @health

20 @health = @tank.health.health

21 @health_image = Gosu::Image.from_text(

22 $window, "Health: #{@health}", Utils.main_font, 20)

23 end

24 @health_image

25 end

26

27 def stats_image

28 stats = @tank.input.stats

29 if @stats_image.nil? || stats.changed_at <= Gosu.milliseconds

30 @stats_image = Gosu::Image.from_text(

31 $window, "Kills: #{stats.kills}", Utils.main_font, 20)

32 end

33 @stats_image

34 end

35

36 def fire_rate_image

37 if @tank.fire_rate_modifier > 1

38 if @fire_rate != @tank.fire_rate_modifier

39 @fire_rate = @tank.fire_rate_modifier

40 @fire_rate_image = Gosu::Image.from_text(

41 $window, "Fire rate: #{@fire_rate.round(2)}X",

42 Utils.main_font, 20)

43 end

44 else

45 @fire_rate_image = nil

46 end

47 @fire_rate_image

48 end

49

50 def speed_image

51 if @tank.speed_modifier > 1

52 if @speed != @tank.speed_modifier

53 @speed = @tank.speed_modifier

54 @speed_image = Gosu::Image.from_text(

55 $window, "Speed: #{@speed.round(2)}X",

56 Utils.main_font, 20)

57 end

58 else

59 @speed_image = nil

60 end

61 @speed_image

62 end

63

64 def draw

65 if @active

66 @object_pool.camera.draw_crosshair

67 end

68 @radar.draw

69 offset = 20

70 health_image.draw(20, offset, 1000)

71 stats_image.draw(20, offset += 30, 1000)

72 if fire_rate_image

73 fire_rate_image.draw(20, offset += 30, 1000)

74 end

75 if speed_image

76 speed_image.draw(20, offset += 30, 1000)

77 end

78 end

79 end


To use it, we need to hook into PlayState:

class PlayState < GameState

# ...

def initialize

# ...

@hud = HUD.new(@object_pool, @tank)

end

def update

# ...

@hud.update

end

def draw

# ...

@hud.draw

end

# ...

end

Assuming you have mocked @tank.input.stats.kills in HUD, you should get a neat view showing interesting things in top-left corner of the screen:

Shiny new HUD

Shiny new HUD