Regenerate health question of logic

New to gaming world so bear with me.

Why on earth would I NOT regenerate to maxHealth when I level up…?

In fact why wouldn’t I get the new levels maxHealth? (I did just level up bonus number 1 health right?)

I can understand in some game logic this could be true…
And that is what my question is.

When or what game type would I only regenerate to a percentage of max?

Thanks for your inputs

This comes down to two things; balance and experience you want to give to the player. Let me give you two examples of 2 RPGs that have different takes on this and why they chose to do it that way.

Pokémon - Won’t regenerate health when leveling up

In these games, you are a person that goes on an adventure. One of the difficulties is to make decisions, for instance, there are difficult parts in the game that are quite long and your Pokémon might hurt, to restore their health you’ll have two options, use your items if carrying any or backtrack to a Pokémon Center, there’s no other way. With a system that restores the Pokémon’s health when leveling up, the game would be far easier, making it way too boring.

Diablo Franchise - Regenerates health when leveling up

In those games your character will restore its full health when leveling up, there are several reasons for that but here are the main two: 1) Diablo is an action RPG, you don’t want the player to stop every time it gets hurt, you want the player to keep playing, 2) when leveling up a cool animation triggers, telling the player that it has become more powerful, restoring health and also dealing massive damage to enemies around. This is used to give the player a sensation of progression and achievement.

Which method to use comes down to game design, you have to be clear as to what type of game you want to create to pick the right choice, you also have to take into consideration your other systems, for instance, in Pokémon games there’s no restoration when leveling up because it could also hurt things like items and Pokémon Centers, making those mechanics irrelevant, breaking the economic system of the game.

Try to see the bigger picture, if you don’t have a bigger picture (you are just following the course) don’t get to caught on that until you start developing your own games. If looking for a job as a programmer then don’t worry about either that because that decision is not up to you, that’s the game designer’s job.

1 Like

As Yee said, this is one of those things that varies from game to game. What we’ve provided is one example, but you can set that regeneration percentage to anything you wish.

As a life long player of World of Warcraft, I’m a fan of that Level Up Splash when you gain a level along with that full regen of health and mana, especially when it happens in the middle of a fight for your life in the middle of a battle you had no business getting into in the first place… Nothing quite like being on the brink of death when SPLASH: You have levelled up, and like Bruce Willis in an action movie, you suddenly have that Heroic Second Wind. It can really add excitement to a game.
It’s also hyper unrealistic, and many game designers choose not to go in that direction.

I remember the first time I played a game called Final Fantasy Tactics: The War Of The Lion. After each encounter, the characters are returned to full health (not just at the level up stage). This, of course, represents the idea that between battles, you rested up and healed all your wounds. The twist in War Of The Lion is that if a character died in a battle, and you were unable to reach that character and ressurect them after three turns, then that character would be lost forever. This very realistic game mechanic meant that you really had to be careful about your choices, as they could have long lasting impacts in the game. Fire Emblem had a similar mechanic, permanent death. It forced the player to think. Lots of modern mobile games with encounter based mechanics have no consequences for deaths (other than perhaps wasting energy in resource deprevation games). The strategies you might employ in such games may be very different from War of the Lion or Fire Emblem, as you could do the last gasp and be consequence free.

It’s all in the angle you choose to present. My examples weren’t specifically regarding health regen on level up, but just providing more insight into how this entire category of decisions can affect the game and the player experience. In the end, it’s the player experience that you want to present that let’s you decide how your game will be.

2 Likes

I really appreciate these comments. It makes sense to me even more now as to WHY having the end goal of my game is very much needed at the beginning of the development.

I am just following along with the class and guessing is not a good idea… I am a firm believer that a good plan is what is needed at the start. That story line…

Each of these classes teaches me new ways to be ABLE to use the unity tools and C sharp together.

Not to mention How to use asset packs is tricky as well…

Thanks for helping me see other ideas!

Tell me what you guys think about using asset packs.

Currently what i have been doing now is this.
Drag the asset from the asset pack into the scene.
Right click it and unpack the prefab.
drag into a folder for my prefabs. (to disconnect the connection from asset pack)

Seems like asset packs are built in so many different ways. (I wish there is a section on asset packs)

Again thanks for helping me understand the basics.

SIDE NOTE:

I have been building a object move script that I can use in many of my games.

Maybe you can take a look and tell me what you think?

The idea I have is to be able to use this script to move objects in what ever game.
so far I have used it in many of these classes simply because it moves objects in so many ways.
Trying to fine tune it and hope to hear comments about it.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameObjectsCanMove : MonoBehaviour
{
    #region LOTS OF VARS
    [Header("------------------------------------------------------------------------")]
    [Header("   control the speed with the period tuner (uses the Mathf.Sin(cycles * tau))")]
    [Header("           any distance on any vector where you define the period")]
    [Header("Oscillator -- move [back and forth]")]
    [Header("-----------------------------------------------------------------------")]
    [Header("Basics")]
    [SerializeField] Vector3 oscillatorMovementVector;
    [SerializeField] float oscillatorPeriod = 2f;
    Vector3 oscillatorStartingPosition;
    float oscillatorMovementFactor;
    const float tau = Mathf.PI * 2;
    [Header("   Set this to true to use the waypoints INSTEAD of the Oscillator")]
    [Header("------  Waypoint begin  ------------------------------------------------")]
    [SerializeField] bool moveAlongWaypoints = true;
    [Header("------------------------------------------------------------------------")]
    Transform objectToMove;
    [SerializeField] float speedToMoveWaypoints = 5f;
    [Header("        --->  Time to linger  <---")]
    [SerializeField] float moveDelayWaypoints = 1f;
    [Header("------------------------------------------------------------------------")]
    [Header("              Use this section for giving paths to objects")]
    [Header("         Create an empty gameOject with waypoints as its children")]
    [Header("               Don't forget to place your waypoints!!")]
    [Header("----------------------------- WAYPOINTS ---------------------------------")]
    [SerializeField] List<Transform> waypoints = new List<Transform>();
    [Header("              ------------------------")]
    [Header(" ***  You can mix it up and use Random Waypoints  *** ")]
    [Header("------------------------------------------------------------------------")]
    [SerializeField] bool useRandomWaypoint = false;
    private int currentWaypointIdx = 0;
    [Range(0.1f,1f)]
    [Tooltip("Choose a distance that is close enough to call it good! .. move to the next waypoint if within this distance of waypoint")]
    [SerializeField] float distanceOfWaypointIsCloseEnough = .2f;

    [Header("------------------------------------------------------------------------")]
    [Header("      Change the Speed every N-TH time it hits a waypoint -- for variety")]
    [Header("    Change movement speed every n-th time a waypoint is hit for one trip")]
    [Header("------------------------------------------------------------------------")]
    [SerializeField] bool useEveryNthTime = false;
    [SerializeField] int numberForEveryNthTime = 4;
    [SerializeField] float multiplySpeedBy = 3f;
    public int hitTargetCounter = 0;
    private int rndHit = 0;

    [Header("-----------------------------------------------------")]
    [Header("                          Rotatation")]
    [Header("            Rotate the object on X, or Y, or Both")]
    [Header("-----------------------------------------------------")]
    [SerializeField] bool rotateTheX = false;
    [SerializeField] bool rotateTheY = false;
    [SerializeField] bool rotateTheZ = false;
    [SerializeField] bool rotateTheOpposite;
    [SerializeField] float speedToRotate = 5f;
    [SerializeField] bool rotateAroundTarget = false;
    [SerializeField] GameObject targetToRotateAround;
    [SerializeField] Vector3 axis;
    [SerializeField] float degreesPerSecondToRotate = 20f;

    [Header("-----------------------------------------------------")]
    [Header("                          Scale")]
    [Header("            Scale the object on X, or Y, or Z")]
    [Header("-----------------------------------------------------")]
    [SerializeField] bool scaleTheX = false;
    [SerializeField] bool scaleTheY = false;
    [SerializeField] bool scaleTheZ = false;
    [SerializeField] float speedToScale = 5f;
    [SerializeField] Vector3 scaleIncrement = new Vector3(0.1f, 0.1f, 0.1f);
    [SerializeField] Vector3 scaleMax = new Vector3(10.0f, 10.0f, 10.0f);

    private float scaleXStart;
    private float scaleYStart;
    private float scaleZStart;
    private float scaleXCurrent;
    private float scaleYCurrent;
    private float scaleZCurrent;
    private Vector3 scaleChange;
    private float timeLastScaled;

    [Header("Start and End of route")]
    private bool firstMove;
    private int direction = 1;
    //Dropper
    [Header("------------------------------  Dropper  -----------------------------------------")]
    [Header("       Set timeBetween, min and max for the X and Z, from Y will be your constant")]
    [Header("                            Drop Object(s) objects drop from the Y value")]
    [Header("-----------------------------------------------------------------------------------")]
    [SerializeField] bool dropObject = false;
    [Tooltip("Choose from your prefabs.. Create one if needed, any object with Rigidbody will work")]
    [SerializeField] GameObject myChildObjectPrefabToDrop;
    [SerializeField] float timeBetweenDrops = 3f;
    public int numberOfObjectsDropped = 0;
    private float timeLastDropped;
    [SerializeField] float minXvalue = -50f;
    [SerializeField] float maxXvalue = 50f;
    [SerializeField] float minZvalue = -50f;
    [SerializeField] float maxZvalue = 50f;
    [SerializeField] float fromYvalue = 10f;
    [Header("Clean up work")]
    [SerializeField] float timeToDestroyAfterDrop = 5f;

#endregion
    void Start()
    {
        objectToMove = this.transform;
        oscillatorStartingPosition = objectToMove.position;
        firstMove = true;
        scaleXStart = transform.localScale.x;
        scaleYStart = transform.localScale.y;
        scaleZStart = transform.localScale.z;

        scaleXCurrent = scaleXStart;
        scaleYCurrent = scaleYStart;
        scaleZCurrent = scaleZStart;
        timeLastScaled = Time.time;
        //dropObject = false;
        timeLastDropped = Time.time;
    }
    void Update()
    {
        FollowWaypoints();
        Oscillator();
        RotateAroundTarget();
        RotateIt();
        ScaleIt();
        DropObjectsWithinYourRange();
    }

    void Oscillator()
    {
        if(moveAlongWaypoints) { return; }
        if(oscillatorPeriod <= Mathf.Epsilon) { return; }
        float cycles = Time.time / oscillatorPeriod; //continually growing over time
        float rawSinWave = Mathf.Sin(cycles * tau); //minus one to plus 1 or -1 to +1 will be the result of .Sin
        //recalculated to go from 0 to 1 so we can go from one point to another and NOT be in the center
        oscillatorMovementFactor = (rawSinWave + 1f) /2f; 

        Vector3 offset = oscillatorMovementVector * oscillatorMovementFactor;
        transform.position = oscillatorStartingPosition + offset;
    }

    void RotateAroundTarget()
    {
        if(!rotateAroundTarget) { return; }
        objectToMove.RotateAround(targetToRotateAround.transform.position , axis, degreesPerSecondToRotate * Time.deltaTime);
    }

    void ScaleIt()
    {
        //Scale the x first
        if(scaleTheX) 
        {
            //scaleXCurrent += scaleXIncrement;
            scaleXCurrent += scaleIncrement.x;
            if(scaleXCurrent > scaleMax.x)
            {
                scaleXCurrent = scaleXStart;
            }
        }

        if(scaleTheY)
        {
            scaleYCurrent += scaleIncrement.y;
            if(scaleYCurrent > scaleMax.y)
            {
                scaleYCurrent = scaleYStart;
            }
        }

        if(scaleTheZ)
        {
            scaleZCurrent += scaleIncrement.z;
            if(scaleZCurrent > scaleMax.z)
            {
                scaleZCurrent = scaleZStart;
            }
        }

        if(scaleTheX || scaleTheY || scaleTheZ)
        {
            //scaleChange = new Vector3(scaleXCurrent, scaleYCurrent, scaleZCurrent);
            if(Time.time > timeLastScaled + speedToScale)
            {
                //Debug.Log("scale x is: " + scaleXCurrent + " scale y is: " + scaleYCurrent + " scale z is: " + scaleZCurrent);
                StartCoroutine(ScaleItOverTime());
                timeLastScaled = Time.time;
            }
        }
    }

    IEnumerator ScaleItOverTime()
    {
        yield return new WaitForSeconds(speedToScale);
        scaleChange = new Vector3(scaleXCurrent, scaleYCurrent, scaleZCurrent);
        objectToMove.localScale = scaleChange;
    }

    private void RotateIt()
    {
        float useNthSpeed = 1;
        if(useEveryNthTime)
            useNthSpeed = speedToRotate * multiplySpeedBy;
            else useNthSpeed = speedToRotate;

        if(rotateTheOpposite) useNthSpeed *= -1;
        //Debug.Log("Rotate?");
        if (rotateTheX) { objectToMove.Rotate(0, 0, Time.deltaTime * useNthSpeed);}
        if (rotateTheY) { objectToMove.Rotate(0, Time.deltaTime * useNthSpeed, 0);}
        if (rotateTheZ) { objectToMove.Rotate(Time.deltaTime * useNthSpeed, 0, 0);}
    }
    private void FollowWaypoints()
    {
        if(waypoints.Count < 2) { return; }
        if(!moveAlongWaypoints) { return; }
        if (Vector3.Distance(waypoints[currentWaypointIdx].position, transform.position) < distanceOfWaypointIsCloseEnough)
        {
            currentWaypointIdx++;
            
            if (useRandomWaypoint)
            {
                currentWaypointIdx = Random.Range(0, waypoints.Count);
                if(rndHit % numberForEveryNthTime == 0)
                {
                    hitTargetCounter += 1;
                    rndHit = 0;
                }
                rndHit++;
            }

            if(currentWaypointIdx >= waypoints.Count)
            {
                currentWaypointIdx = 0;
                hitTargetCounter += 1;
            }
        }
        StartCoroutine(MoveItCoRountine(waypoints[currentWaypointIdx]));
    }

    IEnumerator MoveItCoRountine(Transform target)
    {
        yield return new WaitForSeconds(moveDelayWaypoints);
        //Debug.Log(transform.gameObject.name + " is moving");
        transform.position = Vector3.MoveTowards(transform.position, target.position, GetSpeedToMove(speedToMoveWaypoints));
    }

    private float GetSpeedToMove(float speedToMove)
    {
        //(useEveryNthTime && ((hitTargetCounter % numberForEveryNthTime == 0))) ? speedToMove * Time.deltaTime * multiplySpeedBy : speedToMove * Time.deltaTime;
        if(useEveryNthTime && ((hitTargetCounter % numberForEveryNthTime == 0)))
            return speedToMove * Time.deltaTime * multiplySpeedBy;
            else return speedToMove * Time.deltaTime;
    }

    private void DropObjectsWithinYourRange()
    {
        if(!dropObject) { return; }

        if(Time.time > (timeLastDropped + timeBetweenDrops))
        {
            //Drop it
            var positionToDrop = new Vector3(Random.Range(minXvalue, maxXvalue), fromYvalue, Random.Range(minZvalue, maxZvalue));
            try
            {
                numberOfObjectsDropped++;
                //This makes the drops move with the parent object so the min's and max's change with object movement
                //Destroy(Instantiate(myChildObjectPrefabToDrop, positionToDrop, Quaternion.identity, gameObject.transform), timeToDestroyAfterDrop);
                //This makes the drops stay within the world min's and max's change with object movement
                Destroy(Instantiate(myChildObjectPrefabToDrop, positionToDrop, Quaternion.identity), timeToDestroyAfterDrop);
            }
            catch (System.Exception)
            {
                Debug.Log("Again this exception thing");
            }
            timeLastDropped = Time.time;
        }
    }
}

I am working slowly on building a scene to demo this universal and versatile script.
Your ideas will help me a lot.
Thanks

There is a lot going on in this script.

I would tend to break each of these up into individual scripts for each function… a FollowWayPoint.cs, Oscillator.cs, RotateAround.cs, etc. This follows what we call the Single Responsibility Principle, and ultmately makes a codebase easier to maintain.

Thanks for this Brian

The main responsibility of this script is to move an object.

For the designer is a question of how to move… right?

This script is a work in progress

In each class I take I have been able to use this script and it gives me comfort to know that this script has a lot of ways to move.

I am learning… and still have a long way to go I know.

Did you look at the code in that progression VBA workbook?

Thanks

As long as it’s working, then it’s fine. One component to add, and probably easier for the designer not to hunt for components. The way I would have done this would require the designer to add each required movement component manually, a sort of modified strategy pattern.

I haven’t had a chance to deep dive into the VBA code, as I’ve been focusing the lion’s share of my spare time on rewriting the saving system to use Json instead of the BinaryFormatter, (with this last draft hopefully summing it up).

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.

Privacy & Terms