Author Topic: Graphical health meter  (Read 8951 times)

0 Members and 1 Guest are viewing this topic.

Offline gumby

Graphical health meter
« on: January 09, 2013, 08:36:46 PM »
Here's a really simple solution to accomplish this.  First, the laborious part; you need to make a view starting with a health bar at 'full strength', and then in each frame remove one 'line' of heath.  My implementation used a health meter with a length of roughly 100 pixels (so 100 cells in the view).  We will instantiate this as our 'lifeMeter' view.

Once you have your view created, add this code to your room script:

Code: [Select]
   (local
        flag
    )

    (instance public rm001 of Rm
       (method (init)
           ...
           (lifeMeter:init())      // display the life meter on the screen
           = flag FALSE            // init the flag variable
       )

       (method (doit)
           (super:doit)
                // when we hit particular cells in the view, display messages to the user
(switch (lifeMeter:cel())
  (case 35
    (if (not flag)
       Print("You don't feel so well.")
       = flag TRUE
  )
  )
  (case 70
    (if (not flag)
       Print("You are doubling over in pain.")
       = flag TRUE
  )
  )
  (case 95
    (if (not flag)
       Print("Death is imminent.")
       = flag TRUE
  )
  )
  (case 100
    (if (not flag)
       Print("You are dead.")
       = flag TRUE
  )
  )
  (default               // need this so that for the 10 seconds that this cell is active
    = flag FALSE   // for the states specified in the switch above that the message is
                                          // only displayed once
  )
)
        )
    )

    (instance RoomScript of Script
      (method (changeState mainState)
           (var currentCel)
           = seconds 10  // wait 10 seconds
           = currentCel (lifeMeter:cel())   // get the current cell of the health meter view
           (lifeMeter:setCel(+ currentCel 1))  // advance to the next view in the cell, life meter is now shorter
      )
    )

    (instance lifeMeter of Prop (properties y 8 x 165 view 11))  // meter located top-center of screen

Basically, the changeState() method runs continuously, every 10 seconds it shortens the health bar by advancing the view to the next cell.  The doit() method waits for the view to hit certain cells, and when it does it alerts the user of their failing heath.

Obviously, this is a really simple implementation, suitable perhaps if the ego is poisoned and steadily losing health, or maybe dying from starvation.  If you wanted something that was more 'event-driven', you could go without the changeState() entirely, and handle the changing of the lifeMeter view through specific events in your game (monster attack, ego touches a particular control color, etc).
« Last Edit: January 09, 2013, 09:20:32 PM by gumby »


In the Great Underground Empire (Zork port in development)
Winter Break 2012 Rope Prop Competition

Offline Cloudee1

Re: Graphical health meter
« Reply #1 on: January 10, 2013, 06:04:22 PM »
Did you use a view with 100 cels?

One small addition, if you ever change the health by more than an increment of 1, then there is a chance you will miss your marks and not display the messages. Likewise, you may actually jump past the 100 mark and not die.

What I would suggest is actually using flag as an integer instead of a boolean. Then you can use < than instead of an = sign to check for print messages against the flag variable to see if the print statement needs to display or not. Also, may as well stick in a variable to see if the health should be dropping or not, as well as a default value for the health.

Code: [Select]
(local
  flag = 0
  counter = 0 // used to count, if carried from room to room place in main.sc
  counterMax = 15 // how many cycles worth of time equals 1 health bar
  health = 100 // if this is carried from room to room, place in main.sc
  doHealthDrop = TRUE // likewise, if carried from room to room place in main.sc
)
...
(method(doit)
(var dyingScript) // If you are going to use the death handler, don't forget this
(super:doit)

  (lifeMeter:cel(health)) // keeps the health bar updated to correct cel

  (if(doHealthDrop) // only do this stuff if we are counting health
    // handle the timer here... if this spans rooms place this bit in the main.sc doit method
    ++counter
    (if(counter > counterMax) // counts cycles
       --health // subtract 1 from health
      = counter 0 // reset cycle counter
    )
    // when the health falls below certain levels, display messages.  
    (switch(flag) // finds out just how bad off we are known to be
       (case 0 // As far as we know everything is fine...
        (if(< health 65)
           Print("You don't feel so well.")
           = flag 1
        )
       ) //ends case 0
       (case 1
        (if(< health 30)
           Print("You are doubling over in pain.")
           = flag 2
        )
       ) //ends case 1
       (case 2
        (if(< health 5)
           Print("Death is imminent.")
           = flag 3
        )
       ) //ends case 2
       (case 3
        (if(< health 1)
           Print("You are dead.")
           = flag 4 // just to get us out of this switch
          // Rather than this, you could always call the death handler instead
          // = dyingScript ScriptID(DYING_SCRIPT)
          // (send dyingScript: caller(3)register("You're dead"))
          // (send gGame:setScript(dyingScript))
        )
       ) //ends case 3
     )//ends flag switch
  )//ends if doHealthDrop TRUE
)// ends doit method

My implementation is slightly different, I count down from 100. I also stuck everything in the doit method instead of making use of the changestate method too. One reason that I added in the doHealthDrop variable, is that during cutscenes or animations, those could go awry with the doit method dropping the health. It would be better off to stop the timer by setting the doheathDrop variable to FALSE when any cutscene animations start and then simpy setting it back to TRUE as the animation ends. All in all though the same basic idea is the same, just the implementation is slightly different. One thing that I wouldn't do however is actually use a view with 100 cels. Instead I would probably use one with 10 or 20 cels and then probably divide the health by however many cels I made.

Code: [Select]
= healthBarCel (/  health 20) // where healthBarCel is another local or global variable depending on scope
(lifeMeter:cel(healthBarCel)) // instead of what I have in the doit method now.

You should also be able to see yet another variation in the source code of aquarius. There I also took into account whether or not the ego was actually moving. While standing still health is regained, while moving health is lost.
Halloween Competition Brass Lantern Prop Competition Groundhog Day Competition

Offline gumby

Re: Graphical health meter
« Reply #2 on: January 10, 2013, 08:05:42 PM »
Yeah, I ended up with a view with about 100 cels.  I see, your implementation uses a counter and only decrements the health every X counts.  I started with this and then simplified it (I had a hard time working out the variables, timing, state, print messages, etc), eliminating the counter entirely and just using the changeState() method to indicate the speed in which the health meter is shortened and use the current cel of the view for the switch statement in the doit().  There's a one-to-one relationship between the current health value and the current cel of the view.

Good call on the inclusion of the death handler, I left it out and upon reflection I probably should not have.

I'll have to check out Aquarius to see how you did it in there.  That technique should probably be posted here too for further completeness.
« Last Edit: January 10, 2013, 08:15:12 PM by gumby »
In the Great Underground Empire (Zork port in development)
Winter Break 2012 Rope Prop Competition

Offline Collector

Re: Graphical health meter
« Reply #3 on: January 11, 2013, 04:00:38 AM »
Wouldn't a view with 100 cels be risking an out of hunk error? If so, perhaps if the view was broken up between, say four separate views (first view for 0%-25%, 2nd for the 26%-50% range, 3rd for the 51%-75% and 4th 76%-100%) and replace the view with the next one when your value went beyond the range of the current view?
KQII Remake Pic

Offline gumby

Re: Graphical health meter
« Reply #4 on: January 11, 2013, 08:10:57 AM »
That's a good question.  Is the issue with a large cell count or the overall size of the view?  I can say that I do have a larger view in my game, but with about half the number of cels.
In the Great Underground Empire (Zork port in development)
Winter Break 2012 Rope Prop Competition

Offline Cloudee1

Re: Graphical health meter
« Reply #5 on: January 11, 2013, 05:30:13 PM »
That's what worries me about a view with so many cels, memory issues. It may be a moot point and even after displaying every cel of the view there may not be a large impact. One way to test... Gumby if you have such a health meter view created already. init the view and setCycle to fwd so that it will display every cel in the view. Right when opening the room, check the memory usage and after it has cycled through the whole loop a time or two check the memory usage again and see if it has created any sort of drain. I'm not sure, but for some reason I have it in my head that the view in it's entirety is not loaded into memory, just the cels of the views as they are needed, that's why I suggest comparing memory to before it has cycled and after. I could be way off on this, but for some reason that's the working model I have.

Another alternative that might work if it is an issue, draw the health bar at full strength and then addToPic a one pixel wide cover every time the health is lowered. If done right it should work without affecting hunk, if done wrong, it would create problems in heap. That would take some testing to get it right though so I'm not going to start typing in code without checking it out first.

Also, we might just be able to look at the code of the speed and volume guages and just use some text like it does to create a working meter.
Halloween Competition Brass Lantern Prop Competition Groundhog Day Competition

Offline gumby

Re: Graphical health meter
« Reply #6 on: January 11, 2013, 08:10:21 PM »
Looks like it loads the whole view into 'hunk space' upon init.  Which in my case consumed 31K (which is the size of the view reported by Companion), but I still have plenty of hunk left (128K) and that's with lots of other views already init()ed.  Doesn't seem to matter whether the view cycles or doesn't, no change there.

You'll all see the health meter in action in a couple of days :)
In the Great Underground Empire (Zork port in development)
Winter Break 2012 Rope Prop Competition

Offline Collector

Re: Graphical health meter
« Reply #7 on: October 30, 2013, 08:02:56 PM »
Hey Gumby, this did not get on the Wiki, would you mind adding the entry with your final code?
KQII Remake Pic

Offline gumby

Re: Graphical health meter
« Reply #8 on: November 01, 2013, 11:11:36 PM »
No problem, done.
In the Great Underground Empire (Zork port in development)
Winter Break 2012 Rope Prop Competition

Offline Collector

Re: Graphical health meter
« Reply #9 on: November 02, 2013, 12:56:17 AM »
That should mostly make the Wiki up to date, unless there is something that I have missed.
KQII Remake Pic


SMF 2.0.19 | SMF © 2021, Simple Machines
Simple Audio Video Embedder

Page created in 0.055 seconds with 15 queries.