RetroGameDev

Best way to implement upgrades/powerups (Phaser 3)

Recommended Posts

Hi everyone,

Looking for some advice on the approach to coding multiple powerups/upgrades. I have googled how to do this but most answers are in c#/java or based on unity so I have trouble understanding what there doing.
I am making a geometry wars/asteroids clone and I am giving the player upgrades when they defeat a boss or reach certain multipliers/kills. The problem is that I am having to change lots of code and using lots of if statements to see if a powerup has been activated then changing the bullet effect and then having to change the collision function for the bullets hitting the enemies so that each powerup does something different.

For example in my powerup file (handles bullet changes when new power is activated and powerup pick ups) I add more if statements to the bullet logic to change for example the bullet speed and texture then I go into the file that handles collisions and make changes to the bullet / enemies function so that if the bullet is a fire bullet for example, the enemies particle effect is changed to red from blue. 

I know there are better ways to do this but I'm not sure how in javascript (or any language) any help would be really appreciated 

 

some code:

powerups.js (bullet logic handler function for special bullets)

const scene = this.scene;
const utils = this.utils;
const player = scene.player.sprite;
if (bullet && utils.playerDied === false) {
 
if(utils.explodingBullet) {
if(utils.specialLevel > 1) {
utils.bulletSpeedSpecial = 120;
}
else {
utils.bulletSpeedSpecial = 250;
 
}
bullet.setTexture('explodeBullet');
}
 
else if(utils.flameBullet) {
utils.bulletSpeedSpecial = 75;
bullet.setTexture('orange');
if(utils.specialLevelCheck === false) {
utils.specialLevelCheck = true;
scene.flameEmitter.explode();
 
}
if(utils.specialLevel > 1) {
player.setTexture('playerO');
scene.playerTrailEmitter.setFrame(['white', 'orange']);
}
 
}
 
else {
bullet.setTexture('bullet');
player.setTexture('player');
scene.playerTrailEmitter.setFrame(['white']);
 
}
 
bullet.fire(player, scene.reticle);
 
utils.lastFiredSpecial = time + utils.bulletSpeedSpecial;
scene.physics.add.overlap(scene.baddieGroup, bullet, scene.callBacks.shootingBaddies, null, scene);
 
 
 

Share this post


Link to post
Share on other sites

I think this is an awesome question, and it the sort of thing everybody runs in to: "How to structure your application to make coding up your logic as easy as possible.".

First up, there is 0 wrong with what you're doing. You've already split your code base up in to logical chunks: something handles bullets, something else handles collisions etc etc. That's great.

Think how you could further split things up so that, ideally, you make changes in a minimum amount of places. Actually, with just a couple of places to change how different types of bullets (and any modifiers applied to them) are handled, you're doing pretty well already.

There are many many different ways you could attempt to solve this problem.

Personally, I reckon I'd start thinking more about the data structures I'm passing around and what each different aspect of my system needs to run.

You could start structuring your data to be more descriptive about the types of things.

Maybe a bullet entity could contain all the variables that your bullet handling and collision code needs to work such as the texture that should be set and the colour or type of any particle emitter that gets generated? Maybe there should be a variable that declares the speed?

This way you just pass this structured object into your functions and they don't care on the specifics of what type they are, they infer what to do based on the properties of that object i.e. rather than those functions knowing that a 'flame bullet' travels at 120 and a 'exploding bullet' travels at 100, it just takes a bullet object that has a speed and uses that to set velocity. 

Then, when you apply a 'power-up', whatever function that applies the power-up could either add the modifiers or manually change the bullet properties.

i.e.

var flameBullet = {
  speed: 120
}

var speedPowerUp = {
  modifier: 20
}

function applyPowerUp (bullet, powerup) {
  return {
    ...bullet,
    speed: bullet.speed + powerup.modifier
  }
}

I've used object-spread there, that's fairly new to JS, all it does it create a copy of `bullet` but then applies a new `speed` property, which takes precedence over what was previously defined in the bullet passed in. There is no need to do it like this, maybe you just store the modifier and calculate the speed later. There's no one single right answer.

Evaluate which method best suits your needs, both of your system and you (or the team) as a programmer.

There's also no need to use a separate function like this, maybe you like classical coding (using classes, careful with these in JS) and you want that function to live on the powerup or bullet objects, that's absolutely fine as a solution.

It sounds like you have divided and structured your code and logic, it also sounds like maybe you just need to do this for the data that logic operates on and you'll be most of the way there to a solution that fits you.

 

 

 

Share this post


Link to post
Share on other sites

Hi mattstyles,

Thank you for the detailed response. I'm glad I was on the right path so I won't have to make many changes.
I think adding and passing in the structured objects into an entity would be a great way to handle this, I was doing something similar before but with lots of more bloat so doing it this way will help a lot.
I have put off finishing the power ups but knowing I'm on the right path and your suggestions will definitely help in finishing the game.


Thanks again,

 

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.