ZERO Sievert

ZERO Sievert

Simple Healthbar
Kalomix 10 月 18 日 下午 8:49
Code Optimize
Hi, I've tried to improve this mod's code to optimize performance as much as possible. Feel free to use any of the code functions if needed.
I'm not very familiar with Gamemaker's workflow or the Catspeak language, so there might be areas that still need optimization. Sorry in advance.

The main changes are as follows:

The HP bar will only appear when an NPC takes damage, and it lasts for 60 frames. If the NPC takes damage again, the duration of the HP bar will reset.
This design not only reduces the number of HP bars drawn but also prevents players from exploiting permanently visible HP bars to quickly detect enemies, which could be considered cheating.
The amount of damage taken by the NPC will be displayed as a light red bar, which shortens quickly after 20 frames and disappears completely after 40 frames.

The frequency of calling the ObjectGetAllInstances() method has been reduced to once every 20 frames.
NPCs with an X-distance greater than 400 or a Y-distance greater than 225 from the player will not have their HP bars calculated or displayed.
For newly detected NPC instances, their current HP will be taken as their Max HP.

The maximum width of an NPC's HP bar is determined by calculating the ratio of the NPC's Max HP, the sprite width, and the player's Max HP. Multi-row HP bars are no longer displayed.
For example, a rabbit's maximum HP Bar Width is only one small segment, a human's maximum Bar Width will not exceed 32, and BIG's maximum Bar Width will not exceed its own sprite width.
If an NPC's Max HP is more than twice the player's Max HP, the NPC's HP Bar Height will be slightly increased to make it appear thicker.

SpriteLoad("s_pixel", "Sprites/pixel.png", 1, 0, 0, 0) ObjectCreate("HealthBar") ObjectSetScript("HealthBar", "create_event", func(_inst) { _inst.depth = -6000 _inst.color_low = #ff0000 _inst.color_mid = #ffae00 _inst.color_high = #00ff00 _inst.color_damage = #900000 _inst.low_hp_ratio = 0.2 _inst.mid_hp_ratio = 0.55 _inst.max_bar_width = 32 _inst.bar_height = 2 _inst.bar_alpha = 0.7 _inst.max_npc_draw = 20 _inst.sprite_img = SpriteGet("s_pixel") _inst.draw_buffer = [] _inst.frame_step = Floor(Random(20)) with obj_player { _inst.player_max_hp = self.hp } }) ObjectSetScript("HealthBar", "step_begin_event", func(_inst) { if _inst.frame_step > 0 { _inst.frame_step -= 1 return } _inst.frame_step = 20 _inst.draw_npc_list = [] let npc_list = _ObjectGetAllInstances(obj_npc_parent) let index = ArrayLength(npc_list) - 1 let npc_draw_count = _inst.max_npc_draw while (index >= 0 and npc_draw_count > 0) { let npc = npc_list[index] if !StructExists(npc, "HealthBarInfo") { let bar_max_width = Max(Abs(npc.sprite_width), _inst.max_bar_width) bar_max_width = Min(Round(bar_max_width * npc.hp / 80), bar_max_width) let bar_offset_y if (npc.npc_id == "ghoul" or npc.npc_id == "ghoul_2") { bar_offset_y = Abs(npc.sprite_height) * 3 / 10 } else { bar_offset_y = Abs(npc.sprite_height) * 5 / 10 } let bar_height = _inst.bar_height -- if the npc's max-hp is higher than player's, slightly thicker the health bar. bar_height = Clamp(bar_height * npc.hp / _inst.player_max_hp, bar_height, bar_height * 2) npc.HealthBarInfo = [ npc, -- 0, npc npc.hp, -- 1, max_hp npc.hp, -- 2, previous_hp Round(bar_max_width / 2), -- 3, bar_offset_x bar_offset_y, -- 4, bar_offset_y bar_max_width, -- 5, bar_max_width [ -- 6, draw_info npc.x - bar_max_width / 2, -- 0, draw_x npc.y - bar_offset_y, -- 1, draw_y bar_max_width, -- 2, draw_width bar_height, -- 3, draw_height _inst.color_high, -- 4, draw_color npc.image_alpha * _inst.bar_alpha, -- 5, draw_alpha 0, -- 6, current_frame 0 -- 7, current_damage ] ] } -- gamedata/npc_preset.json -> alert_visual_distance -> human_far -> 300 if (Abs(_inst.x - npc.x) > 400 or Abs(_inst.y - npc.y) > 225) { index -= 1 continue } ArrayPush(_inst.draw_npc_list, npc.HealthBarInfo) index -= 1 npc_draw_count -= 1 } }) ObjectSetScript("HealthBar", "step_end_event", func(_inst) { with obj_player { _inst.x = self.x _inst.y = self.y } let index = ArrayLength(_inst.draw_npc_list) - 1 while (index >= 0) { let npc_info = _inst.draw_npc_list[index] let npc = npc_info[0] let draw_info = npc_info[6] if (!InstanceExists(npc) or npc.hp <= 0) { if (draw_info[6] > 0) { draw_info[6] = 0 draw_info[5] = 0 } ArrayDelete(_inst.draw_npc_list, index, 1) index -= 1 continue } if (npc.image_alpha == 0) { if (draw_info[6] > 0) { draw_info[5] = 0 } index -= 1 continue } if (npc.hp == npc_info[2]) { if (draw_info[6] > 0) { draw_info[0] = npc.x - npc_info[3] draw_info[1] = npc.y - npc_info[4] draw_info[5] = npc.image_alpha * _inst.bar_alpha } index -= 1 continue } if (draw_info[6] > 0) { draw_info[7] = npc_info[2] - npc.hp + DamageFrameCalc(draw_info[7], draw_info[6]) } else { draw_info[7] = npc_info[2] - npc.hp ArrayPush(_inst.draw_buffer, npc_info) } npc_info[2] = npc.hp let hp_ratio = npc.hp / npc_info[1] let draw_color if (hp_ratio > _inst.mid_hp_ratio) { draw_color = _inst.color_high } else if (hp_ratio > _inst.low_hp_ratio) { draw_color = _inst.color_mid } else { draw_color = _inst.color_low } draw_info[0] = npc.x - npc_info[3] draw_info[1] = npc.y - npc_info[4] draw_info[2] = Ceil(hp_ratio * npc_info[5]) draw_info[4] = draw_color draw_info[5] = npc.image_alpha * _inst.bar_alpha draw_info[6] = 60 index -= 1 } }) ObjectSetScript("HealthBar", "draw_normal_event", func(_inst) { let index = ArrayLength(_inst.draw_buffer) - 1 while (index >= 0) { let npc_info = _inst.draw_buffer[index] let draw_info = npc_info[6] if (draw_info[5] > 0) { let alpha = draw_info[5] * (if (draw_info[6] >= 20) { 1 } else { draw_info[6] / 20 }) DrawSpriteStretchedExt( _inst.sprite_img, 0, draw_info[0], draw_info[1], draw_info[2], draw_info[3], draw_info[4], alpha ) let damage = Floor(DamageFrameCalc(draw_info[7], draw_info[6])) if (damage > 0) { DrawSpriteStretchedExt( _inst.sprite_img, 0, draw_info[0] + draw_info[2], draw_info[1], npc_info[5] * damage / npc_info[1], draw_info[3], _inst.color_damage, alpha * 0.9 ) } } draw_info[6] -= 1 if (draw_info[6] <= 0) { draw_info[7] = 0 ArrayDelete(_inst.draw_buffer, index, 1) } index -= 1 } }) EventAssignFunction("exit_train", func() { with obj_player { ObjectSpawn("HealthBar", self.x, self.y) } }) DamageFrameCalc = func(damage, current_frame) { if (damage == 0 or current_frame < 20) { return 0 } if (current_frame > 40) { return damage } return damage * (current_frame - 20) / 20 } _ObjectGetAllInstances = func(arg) { let a_npcs = [] with arg { ArrayPush(a_npcs, self.id) } return a_npcs }