- Home /
Is there any good way to make my Power-Ups System?
I'm sorry if I can't find better wording for my question.
In my game, I want to have a Power-Ups System, which simply changes the player between multiple dedicated Power-Ups that cannot be combined together. The change should occur when another script calls this Script's ChangePowerUp()
method. But my problem is, that, every time I want a new Power-Up I need to copypaste and change around this part of the code:
else if (collectedPowerUp == PowerUp.ExtraJump)
{
playerJumpScript.jumpVelocity = extraJumpVelocity;
}
I then also need to add a new Element to the PowerUp
enum, and I need to introduce a new & slightly differently named variant of the jumpVelocity
variable and every other variable that Power-Up should change. And I already dread how much of a pain it would be to work with when I'd want to introduce more suffisticated Power-Ups.
It looks something like this in the Inspector:
is there any way to change it so that I can simply add new Power-Ups by increasing another variable, like with the ,,Size" variable on the Inputs List?
This is the complete Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PowerUpsCatalogue : MonoBehaviour
{
enum PowerUp {DefaultState, ExtraJump};
public PlayerJump playerJumpScript;
[Header("Default State")]
public float defaultJumpVelocity = 600f;
[Header("Extra Jump")]
public float extrajumpJumpVelocity = 800f;
void ChangePowerUp (PowerUp collectedPowerUp)
{
//Default State
if (collectedPowerUp == PowerUp.DefaultState)
{
playerJumpScript.jumpVelocity = defaultJumpVelocity;
} //Extra Jump
else if (collectedPowerUp == PowerUp.ExtraJump)
{
playerJumpScript.jumpVelocity = extrajumpJumpVelocity;
}
}
}
I've found it easier to have a base / unmodified copy of the stats you intend to change with powerups and then an "effective" / "current" version where the manipulations are active. You'd then keep track of which powerups are active and recalculate the "effective" ones each frame.
$$anonymous$$eeping track of what's been modified when you only have one version gets a bit tedious.
Answer by Ossi101 · Jul 25, 2019 at 01:25 AM
I would use ScriptableObjects to create and manage your power-ups. This way you can separate the effects of your power-ups from the logic of choosing them while avoiding some duplicate code and tedious code variations. You can also make more types of power-ups without having to write any extra code if your effects are simple.
First you need to create the power-up ScriptableObject that's going to house the data you want to manipulate on your player. In this case I'm going with the same thing you're doing and affecting the player's jump velocity. With this class you can create asset files in your project window that represent the data that your power-ups will have, and that will then be applied to the player.
using UnityEngine;
[CreateAssetMenu(fileName = "NewJumpPowerUp.asset", menuName = "Power Ups/Jump")]
public class JumpPowerUp : ScriptableObject
{
public float velocity = 0.0f;
}
I have a Collectables class that will be placed on a power-up in the game world for the player to collect. This will have the logic to detect if the player has collided with it then call the appropriate method on the player class to apply the power-up effect.
using UnityEngine;
public class Collectable : MonoBehaviour
{
public JumpPowerUp jumpPowerUp = null;
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Player"))
{
collision.GetComponent<PlayerWithPowerUp>().ApplyPowerUp(jumpPowerUp);
Destroy(gameObject);
}
}
}
And lastly the player script where I have my ApplyPowerUp() method that takes a JumpPowerUp and the logic to apply that to whatever local variables are relevant to make the effect work.
using UnityEngine;
public class PlayerWithPowerUp : MonoBehaviour
{
public float jumpVelocity = 2.0f;
private Rigidbody2D myRigidbody2D = null;
private bool isJump = false;
private float xDirection = 0.0f;
private void Awake()
{
myRigidbody2D = GetComponent<Rigidbody2D>();
}
private void Update()
{
xDirection = Input.GetAxisRaw("Horizontal");
if (Input.GetKeyDown(KeyCode.Space))
{
isJump = true;
}
}
private void FixedUpdate()
{
myRigidbody2D.AddForce(new Vector2(xDirection, 0.0f) * 10.0f);
if (isJump == true)
{
myRigidbody2D.AddForce(new Vector2(0.0f, jumpVelocity) * 100.0f);
isJump = false;
}
}
public void ApplyPowerUp(JumpPowerUp jumpPowerUp)
{
jumpVelocity = jumpPowerUp.velocity;
}
}
The great thing about using ScriptableObjects in this way is that you can create as many variations of this JumpPowerUp as you want, with different kinds of velocities without having to write more code. You also don't have to check what type of jump power-up it is. All you have to do is give it to whatever script can handle this data type.
Here is the result of the code. The red power-up debuffs the player and make their jump velocity smaller while the green power-up buffs them by doubling it instead.
If you haven't used ScriptableObjects before I made a video response talking in more detail about this code and how it works. I also show a quick and simple way you can use it in a list. There are lots of possibilities! I hope this helps! Good luck!