Class method to destroy enemy mesh when its health reaches zero [CODE INSIDE]

I have my enemy loaded in a list and when I shoot I check what picked mesh’s name is against said list and I subtract 1 health like :

if(evt.button == 0){
//Play current Weapon’s animation
scene.beginAnimation(player.gunLoadout[currentWeapon].mesh, 0, 100, false);
// Remove ammunition
if(player.gunLoadout[currentWeapon].ammo > 0){
player.gunLoadout[currentWeapon].ammo -= 1;
}

            // Update HUD
            hud[2].text = String(player.gunLoadout[player.currentWeapon].ammo);               
            // Destroy camera's ray target in 1000 distance
            let ray = camera.getForwardRay(10000);
            let hit = scene.pickWithRay(ray);
            let model = hit.pickedMesh;             
            // Exempt ground from the be shot at
            if(hit !== null && model !== null && model.name != "ground"){

                for(let i=0; i<enemyList.length ; i++){
                    if(enemyList[i].name == model.name){
                        console.log("Target Hit :" + model.name + " Health :" + enemyList[i].health );
                         enemyList[i].health -= 1;
                    }
                }                    
            }                               
        }

Following is the code of my enemy class. I want to destroy the enemy mesh when it detects that its health reaches zero using the destroy method. The thing is I can’t seem to pass the correct health inside the registerBeforeRender part, all I’m doing is passing the health that the object has upon creation, and if I try to use the this.health variable inside the registerBeforeRender “loop” I get that it’s not defined.

export default class Enemy{
    constructor(scene, name, mesh){
        // Game properties
        //this.game = game;
        this.scene = scene;
        // Enemy properties
        this.name = name;
        this.mesh = mesh;        
        this.health = 5;
        
        this.move();
        this.destroy();
    }

    move(){        
        let mesh = this.mesh;       
        let scene = this.scene;
        let camera = scene.activeCamera;

        if(mesh){
            this.scene.registerBeforeRender(function(){
                // Calculating distances between the enemy and the player
                let initVec = mesh.position.clone();
                let distVec = BABYLON.Vector3.Distance(camera.position, mesh.position);                
                let targetVec = camera.position.subtract(initVec);
                let targetVecNorm = BABYLON.Vector3.Normalize(targetVec);

                // Move enemy towards the player and stops slightly ahead
                if(distVec > 10){
                    distVec -= 0.1;
                    mesh.translate(targetVecNorm, 0.1, BABYLON.Space.WORLD);                     
                }
                // Enemy always faces the player
                mesh.lookAt(camera.position, Math.PI);          
            });
        }
    }

    destroy(){
        let mesh = this.mesh;
        let health = this.health;
        let scene = this.scene;       

        if(mesh){            
            this.scene.registerBeforeRender(function(){                
                console.log("Health:" + health);
                if(health == 0){
                    scene.getMeshByName(mesh.name).dispose();
                }    
            });
        }
    }
}

It’s obvious that I’m lacking in my JS skills but I know that I’m a bit confused about the context of variables or something. Hope someone skillful can chime in and clear my misunderstandings.

Hi HD! Umm… I have an idea… not sure if you will like, or if it will work.

First, remove destroy() function.

Now, up higher in code… find where you did… enemyList[i].health -= 1;

Just after that…

if (enemyList[i].health == 0) { enemyList[i].dispose() }

Would that work? shrug Just an idea. It is VERY common to check if/not a limit/range has been hit… just after an increment or decrement of a value. In your case, just after reducing health, check if the health == 0 limit/range has been hit.

Note: It is NOT common to use registerBeforeRender inside of a function (except for inside createScene() function, which is very common). After many calls to destroy(), there would be MANY functions “stacked up”… all registered to happen before each frame-render. That would soon slow-down performance. Most programmers would want 3 or less functions… registered before render, and would do those registers OUTSIDE-of functions… at main-line scene code level.

Here’s a search-in-code of our playgrounds database… for registerBeforeRender… so you can see how/where others use registerBeforeRender. I hope I have been helpful. :slight_smile:

2 Likes

Hi again. To go a step further, I THINK you will want some kind of explosion… when an enemy is “disposed-of”. So let’s KEEP your destroy() function, but re-code it a bit.

First, you still need that line up higher… but let’s change it to this…

enemyList[i].health -= 1;
if (enemyList[i].health == 0) { destroy(enemyList[i]); }

Now, let’s make a much simpler destroy(enemy) function:

destroy(enemy) {
    let explodeLocation = enemy.getAbsolutePosition(); // grab the location of enemy ship
    enemy.dispose()  // goodbye ship
    let myRunningAndWaitingParticleSystemManualEmitCount = 5000; // set explosion debris-levels
    // now lets call a soon-to-be-coded generateExplosion function...
    generateExplosion(myRunningAndWaitingParticleSystemManualEmitCount, explodeLocation);
}

BabylonJS particleSystems (ps) can be “started” in “waiting” or “idling” condition. You do this by setting ps.emitRate = null… or = 0… and then call ps.start(). Build and start your particleSystem… EARLY in your code… perhaps just after setting lights and cameras. It will be part of your explosion generator. :slight_smile: It should NOT be built INSIDE OF your destroy() or generateExplosion() function. Do it much earlier and start() it. It will do nothing, because it has no .emitRate and no .manualEmitCount. It is idling… waiting for you to dump particles into it and tell it where to spray them. :slight_smile: Let’s call this idling particle system… “sprayer”.

ParticleSystems have lots of settings, so it will be a screen-full of ps creation code. It takes a little practice to set good spray angles and fly-speeds… and colors… and lifetimes. We have a nice particleSystem learning demo… at https://www.babylonjs-playground.com/#WZZDNR#14

Now, let’s create the generateExplosion function. Remember that ‘sprayer’ is the name of our idling, already-started particle system.

generateExplosion(puffsize, where) {
    sprayer.emitter = where;  // set the spray nozzle to a vector3 where the ship got destroyed
    sprayer.manualEmitCount = puffsize;  // we set this value to 5000, earlier.  Activate idling PS!  PUFF!
}

Easy, huh? We got our destroy() function cleaner, and made it dispose the enemy ship, AND made it call an explosion generator function. The explosion generator is a separate thing from the ship disposer, so it’s easy to change explosion effects. Maybe start TWO idling particleSystems… one to generate fire, and one to generate smoke puffs… and set both their .manualEmitCount to 5000 at the same time. BIG PUFF! PRETTY PUFF! POOM! :slight_smile:

BJS has great particle systems… powerful. They can change particle size and colors AS they age/fly. Very easy to customize. One minor drawback: Because of “depth sorting”… standard particles cannot spray behind AND in-front-of a mesh… at the same time. For variable depth-sorting… you need to use SPS - Solid Particle System. IT can also be told to spray chunks of exploded ship. :+1:

How’s that sound? Not too bad, huh? Good fun! Mistakes in my code-stuff… likely. Good luck, stay in touch.

1 Like

Wow I didn’t expect such a lengthy and full reply. I see yeah the way you suggest to dispose the mesh works but I just wondered if there was something like a listener function that will destroy it from within the object. Few comments about your posts:

1)So as far as the registerBeforeRender function goes, is my usage of it inside my move() function generally a bad practice too ? If you see my previous posts too, a general gist is always that I’m trying to find the “proper” way of doing things. I can’t link to my github page right now cause I have a private repository but I know for sure that my code is amateur and I make stupid decisions cause I’m not that good in JS + Code Design so I try to make things work as fast as possible and when I discover something that can look cleaner,simpler I do it later.

2)Yeah that would probably be my end goal having explosions and all that upon an enemy’s death so it’s very nice to see you so eager about my project that you suggested it yourself in the best way possible by providing examples and pointers. Really didn’t think it would be so easy and via Babylon builtin methods, so I just did it right now it was so straight forward. It’s not easy for me to provide a gif right now but here is a screenshot after the mesh is destroyed :

The little cloud rises a bit and it’s destroyed. I know it can look better but I just went with the defaults.

Amazing ! Your posts were sooo helpful, when I finish my project I will make a big post that will be sure to give credit you and all the others awesome people that helped me along the way !

1 Like

Hi HD, thx for the kind words… I’m glad I am being helpful to you.

  1. Yes, it should probably be removed. Rarely does a registerBeforeRender happen inside a repeatedly-called function, but it COULD happen in an init_game() function. Often… registerBeforeRender would CALL move(), instead-of move() calling registerBeforeRender.

Do you know what a “power take off” (PTO) is… on a farm tractor? It is a tractor-motor-driven shaft that sticks-out the back of the tractor… used to power “extra things” that are attached to the back. RegisterBeforeRender IS that PTO drive shaft… and it rotates a little bit (it steps)… approx every 1/60th of a second (every render frame).

Let’s pretend that you change the name of your move() function… to “update_movements()”. The PTO shaft… and the tractor’s drive wheels… SHARE the master render-loop “motor”… so it is important to NOT have a slow-running function… called by registerBeforeRender. If the registerBeforeRender functions take too long to execute, it will start slowing down the tractor’s drive wheels (FPS rendering speeds). SO… if your update_movements() is slow to finish its work, the entire scene will slow-down.

But, its not worth worrying-about, at this time. Maybe 50,000 enemy ship movements and 20,000 missile-in-flight movements per “step”… would be time to worry… but I don’t think YOUR update_movements… will need to do THAT much work. :slight_smile:

But still, you want update_movements() to update all enemy ship movements (and in-flight bullets/missiles)… quickly. The PTO runs 60 times per second, and if update_movements() is registerBeforeRender… IT will also run every 1/60 of a second. That’s pretty fast. Thus… update_movements() should iterate/step-thru every enemy ship (and flying bullet) on the screen… and move each one… a TINY amount, which is sometimes called “a step”.

Let’s look at this odd playground: https://www.babylonjs-playground.com/#1EBCJY#15

Obviously, we have some “render-loop animation” happening there. (BabylonJS Animation systems can also do this). Our registerBeforeRender is near the bottom of the code… not inside a repeatedly-called function. We have put update_movements() on the render loop (registerBeforeRender)… and it is working nicely… with strong FPS speeds. I think you can imagine how a similar update_movements could move enemy ships across the skies… in little steps. In a way, the term “update” means “take a step”, eh?

Anyway, stare at that playground for a couple days… make changes… do more RUNs and saves… you cannot damage/overwrite anything in the playground. Every time you do a new SAVE in the playground, the URL changes… so… bookmark them, so you can visit your “work playgrounds” easily, later. Before long, you will have a big pile of favorite playground URL’s… and you will use them often.

Let’s stop here… so you have time to look-at and torture-test that playground and all the tests/saves you make with it. Again, you cannot damage the #15 playground, no matter how much you edit it and re-run it… and make more SAVES or grab Zips. You can’t hurt anything… no matter what you do. So, just have full fun without worry… lock-up your browser a few times or crash the playground… it’s all good and happens to everyone sometimes.

There is a newer system in BJS… called “observers” (and observables). You could do that registerBeforeRender… in a different way, if you wish. You can replace line 71 with…

scene.onBeforeRenderObservable.add(update_movements);

Cool, huh? You can also use ‘remove’ in-place-of ‘add’. It’s all about adding and removing updaters/steppers to/from the PTO shaft (render-loop)… building your tractor-engine-powered farm tools and fun. BabylonJS supplies the tractor and its ever-running engine. You are free to hook anything to it… but if you “hang” really slow-to-return functions on it, it will slow down the main tractor engine. Iterating thru a list of 10000 enemy ships… moving each one a tiny bit, AND updating the position of 500 bullet.isFlying = true; bullets… every render loop… is not a problem. JS runs pretty darned fast. It is difficult to slow-down the tractor engine by over-loading its PTO shaft. :slight_smile:

Look at the JS console (f12 dev tools of your browser) while that playground is running. Those numbers are just FLYING down the screen… often going faster than the console can print them, and can cause your CPU fan to speed-up. Disable line 60… and your CPU fan might slow-down again. I use console.log(whatever) for lots of help in debugging programs… it’s our best friend for watching what is happening in a program. I often have 20-30 of them in a piece of code that is under dev… helps me watch stuff. Nice learning tool.

Ok, good luck. Ask more questions if you wish, maybe here, maybe in new question-threads.

2 Likes