#just wanna respawn my enemy lol
1 messages · Page 1 of 1 (latest)
public void Respawn()
{
GameObject NewMan = Instantiate(Respawner);
NewMan.SetActive(true);
NewMan.transform.position = StartPos;
NewMan.transform.parent = transform.parent.parent.parent;
NewMan.GetComponent<Timed2>().Timer = Mathf.RoundToInt(RespawnTime);
}```
NewMan is the Respawner object. it does get instantiated fine, however, when it does, the "ToRespawn" in Timed2 changes to being a missing gameobject
the Respawner object is inside of the prefab initially, if that means something.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Timed2 : MonoBehaviour
{
// Start is called before the first frame update
public int Timer;
public GameObject toRespawn;
public Transform TheParent;
public IEnumerator RespawnIt()
{
yield return new WaitForSeconds(0.1f);
yield return new WaitForSeconds(Timer);
GameObject aNew = Instantiate(toRespawn);
}
void Start()
{
StartCoroutine(RespawnIt());
}
}
Post the entire script containing Respawn()
there's a lot in there that's not used yet. I really thought the respawning part would be the easiest thing lol
The best approach is to Debug.Log() each step
helps figure out what goes wrong
and where
oop this line:
NewMan.GetComponent<Timed2>().TheParent = transform.parent.parent.parent;
is irrelevant as I wound up not using that bit
Alright.
void HasDied()
{
Destroy(transform.parent.parent.gameObject, 0.5f);
}
I believe that when you delete a parent object, it also deletes the children.
yes. but that's why I made a clone of the respawner object first
it gets put into the scene right as the enemy gets deleted
before game starts:
the instant the game starts this happens:
and then when health is 0, there is a Respawner(Clone) in the scene, which has this instead:
You have three options:
- Upload project folder as a compressed archive (except Libraries) so I can make an attempt to fix your unique case
- Recreate the solution from scratch, with a more top-down or Manager approach
- Follow someone else's example from an online article or tutorial
damn bot what
Damn bots blocking reaction gifs even in threads xP
it insta joined the thread too
crazy
it'd probably be best to go for 2. I didn't think it'd be necessary to do that.
<insert Pink Floyd meme>
Leave those threads alone! xD
I know how I'd implement case 2, but then I have to restructure the entirety of how the enemies work
which is a pain
it seems illogical that just because it's in a prefab, it can't refer to the prefab. but there must be something I'm missing
Hm. Maybe ask for a good approach to the Practical Idea of your solution. I don't have that much experience.
everything works except the respawning lol. and it feels a bit scummy to send somebody the entire project just to fix something that I'm probably just oblivious to
Imagine this: One script to rule the entire process.
A dictator parent.
hmm that's a good point actually
the children should only react to the parent's instructions
if I have a general manager for the respawning of enemies and all enemies are contained within, each enemy could respectively call the respawner
it seemed overcomplicated when I thought about it initially but in the long run it's probably a good idea
a dictator parent. nice
Do you want a 30min mini-course in Object Oriented Programming?
an example I recently created - SpriteBlinker
😅 that would be cool, but I am far too broke to get any sort of course rn
the Object Oriented Programming approach is good when there is much interactivity in code. Manager-style.
This is as opposed to using components (MonoBehaviour)
Not strictly necessary in all cases, but can give a better degree of predictability and thus control.
I'm not talking money, just casual conversation
o
create or duplicate a Game Object with a SpriteRenderer, which you can easily see
should I do this in 2d?
Unity works with both 2D and 3D.
A SpriteRenderer is 2D
you just need to be able to see a sprite
good stuff
using UnityEngine;
public class SpriteBlinkComponent : MonoBehaviour
{
[Range(0f, 1f)] [SerializeField] private float alpha = 0.2f;
[SerializeField] private float duration = 5.0f;
[SerializeField] private float frequency = 0.2f;
public bool isBlinking;
private SpriteRenderer sprite;
private Color colorSprite;
private Color colorAlpha;
private bool blink;
private float blinkTimer;
private float effectTimer;
private void Awake()
{
sprite = GetComponent<SpriteRenderer>();
colorSprite = sprite.color;
colorAlpha = new Color(1f, 1f, 1f, alpha);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.B))
{
isBlinking = true;
}
if (isBlinking)
{
Blink();
}
}
private void Blink()
{
effectTimer += Time.deltaTime;
blinkTimer += Time.deltaTime;
if (blinkTimer >= frequency)
{
blinkTimer -= frequency;
blink = !blink;
}
if (blink)
{
sprite.color = colorAlpha;
}
else
{
sprite.color = colorSprite;
}
if (effectTimer >= duration)
{
effectTimer = 0f;
blinkTimer = 0f;
isBlinking = false;
sprite.color = colorSprite;
}
}
}
This is a typical MonoBehaviour script
add it as a component to the game object
This is what it does:
he do be blinkin
When isBlinking is true, it will start to play the Blink() effect
Blink() lasts for a duration
and the Sprite will blink between 0.2f and its original alpha (opacity / transparency)
this is achieved by setting the sprite.color to a different color
the alpha cannot be changed directly when on a sprite
Simple enough
mmk
The OOP (Object Oriented Programming) approach rewrites the MonoBehaviour to a standard C# class, and instantiates it in a Manager type script
using UnityEngine;
public class SpriteBlinker
{
private float alpha = 0.2f;
private float duration = 5.0f;
private float frequency = 0.2f;
private SpriteRenderer sprite;
private Color colorSprite;
private Color colorAlpha;
private float blinkTimer;
private float effectTimer;
private bool blink;
private bool isBlinking;
public float Alpha
{
get { return alpha; }
set
{
alpha = Mathf.Clamp01(value);
colorAlpha.a = alpha;
}
}
public float Duration
{
get { return duration; }
set { duration = value; }
}
public float Frequency
{
get { return frequency; }
set { frequency = value; }
}
public SpriteBlinker(SpriteRenderer sprite, float alpha = 0.2f, float duration = 5.0f, float frequency = 0.2f)
{
this.sprite = sprite;
this.alpha = alpha;
this.duration = duration;
this.frequency = frequency;
colorSprite = sprite.color;
colorAlpha = sprite.color;
}
public void Blink()
{
isBlinking = true;
}
public void Listen()
{
if (isBlinking)
{
effectTimer += Time.deltaTime;
blinkTimer += Time.deltaTime;
if (blinkTimer >= frequency)
{
blinkTimer -= frequency;
blink = !blink;
}
if (blink)
{
sprite.color = colorAlpha;
}
else
{
sprite.color = colorSprite;
}
if (effectTimer >= duration)
{
effectTimer = 0f;
blinkTimer = 0f;
isBlinking = false;
sprite.color = colorSprite;
}
}
}
}
using UnityEngine;
public class PlayerManager : MonoBehaviour
{
private SpriteRenderer sprite;
private SpriteBlinker blinker;
private void Awake()
{
sprite = GetComponent<SpriteRenderer>();
blinker = new SpriteBlinker(sprite);
blinker.Alpha = 0.2f;
blinker.Duration = 2.5f;
blinker.Frequency = 0.2f;
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.X))
{
blinker.Blink();
}
blinker.Listen();
}
}
There are two concepts which might be unfamiliar to you
- Properties (public get set)
- this.variable = variable
uhhh quick question
I applied the scripts and ran it
when I pressed X unity crashed
lol
Properties are best practice when it comes to OOP
In this solution, I use it to validate the input of the setter in Alpha, because simply changing the value doesn't reflect all the changes I need
Hm
the whole screen went black for a sec
That's unexpected. I hope you had saved your project?
Huh.
How old is your computer?
idk. it's a windows 7 pc
if the Harddrive is 6 years old it could fail any day
invest in SSD if you can. Mechanical HDDs are slow.
speeds up the entire computer
oop not sure if this is relevant but I had named it wrong
not sure how that could've caused a crash but
just name things the same things I've named them, unless you have another PlayerManager
the Class name needs to be the same as the Script name
hey how I do the reloading scripts thing or whatever for VS again?
What do you mean?
you mentioned it earlier when I was talking about using the other unity version
I can't remember the right word for it
ah ok there we go
pretty sure I broke Unity somehow when I changed the script cos then all of a sudden it's expecting a mono where theres no mono and it shouldn't have been able to be on an object in the first place lol.
got it working now
In order to test the OOP script - remove the SpriteBlinkerComponent from the game object, and add the PlayerManager
the SpriteBlinker class only needs to exist in a file on the project
looks like it :)
now you could send it to people to break their game
jk jk
but ye works like a charm now
so, at this point, study the difference between the two different approaches - Class vs MonoBehaviour
Any questions about Properties?
ehh I think they make sense logically. I haven't done a whole lot with just Classes but I get the jist of it, ish.
// Fields
private float alpha = 0.2f;
private float duration = 5.0f;
private float frequency = 0.2f;
// Properties
public float Alpha
{
get { return alpha; }
set
{
alpha = Mathf.Clamp01(value);
colorAlpha.a = alpha;
}
}
public float Duration
{
get { return duration; }
set { duration = value; }
}
public float Frequency
{
get { return frequency; }
set { frequency = value; }
}
// This is the Constructor. It is called upon initializing a new SpriteBlinker()
public SpriteBlinker(SpriteRenderer sprite, float alpha = 0.2f, float duration = 5.0f, float frequency = 0.2f)
{
this.sprite = sprite;
this.alpha = alpha;
this.duration = duration;
this.frequency = frequency;
colorSprite = sprite.color;
colorAlpha = sprite.color;
}
this. refers to the sprite variable of the class (fields)
while simply sprite will refer to the Local Variable of the method, which is the Constructor
it was not necessary to use this. as I could just name the Local Variables differently
but it's less tedious, and Visual Studio 2022 makes it very easy to autocomplete when that is used
I see
now, the SpriteBlinker could arguably simply be a component
but if you want other scripts to interact, it would be easier this way, and more controllable
if you need multiple scripts to catch each other's callbacks during Update, you will have to make sure the Logical Execution Order is correct, but in Unity the order is arbitrarily linked to the order of instantiation, as far as I've observed
using an OOP approach negates those issues
so you're suggesting having an OOP thing for managing the respawning of any given enemy
in essense
More than less. If not OOP, I would strongly advise a top-down design.
if you have a SpawnManager, simply tell it that an object is supposed to die, and then the SpawnManager destroys it, and also spawns another
so with a spawnmanager, how would I get the prefab instance of anything inside?
say I have a function in the spawnmanager script that gets a copy of the enemy, destroys the enemy, waits X seconds, and places in the copy.
is that doable dynamically? or would each enemy in this instance require a preset spawn manager?
[assuming not OOP, that is]
so the argument passed to the function would just be like
a respawntime, a position, and the correct prefab
let's say you have a game object named SpawnManager, along with a MonoBehaviour script with the same name
alright
sec
using UnityEngine;
public class SpawnManager : MonoBehaviour
{
public SpawnManager Instance;
[SerializeField] private GameObject myPrefab;
private void Awake()
{
if (Instance == null)
{
Instance = this;
}
else if (Instance != this)
{
Destroy(gameObject);
}
DontDestroyOnLoad(this.gameObject);
}
public void Respawn(GameObject todestroy)
{
// Code to destroy
Instantiate(myPrefab);
}
}
sec, having issues
what is the usage of the Awake()? is that just so its persistent through scenes?
Yes. It creates a static instance of the script, which will be accessible to all other scripts
But there can only be one, which that code ensures.
ah
I get it, but how can this apply to multiple different enemies? say I have idk, a goblin and a warrior. both with different starting locations.
I could do a check in the Respawn void, but then I need to have several myPrefab instances from the start, one for every single enemy
or is there more to this?
It makes things simple, but right now I have no idea why the example I'm creating is not working,
so for now, here's an example from my existing code
private void OnCollisionExit2D(Collision2D collision)
{
// Damage Bricks
if (collision.gameObject.layer == layerBricks)
{
//LevelManager.instance.bricks.Find(x => x.name == collision.gameObject.name).TakeDamage(ballDamage);
string name = collision.gameObject.name;
LevelManager.instance.DealDamageToBrick(name, ballDamage);
DebugLine(name);
}
}
LevelManager.instance.DealDamageToBrick(name, ballDamage);
the idea is that the Manager contains the functions
and other game objects can trigger those functions
by activating them, and passing along information
by making the Manager accessible via a static instance,
it enables the other scripts to access your "game functions" like any other C# or Unity function
this isn't easy to wrap one's head around
I'm currently in the process of rethinking my current project, and I think the next one after that will be fully OOP
but it's very logical, especially since you get to create the language that runs your game
I dig it
the words we use when thinking about logic have a lot to say about how we arrive at a solution
that's why we often answer ourselves when typing out questions xD
is this flawed at all? am I missing any key components here? trying to follow along.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RespawnAnyEnemy : MonoBehaviour
{
public RespawnAnyEnemy Instance;
public List<GameObject> Guys = new List<GameObject>();
GameObject CloneHim;
Transform GottenParent;
private void Start()
{
foreach(Transform Enemy in transform)
{
GameObject Copy = Instantiate(Enemy.gameObject);
Copy.SetActive(false);
Guys.Add(Copy);
}
}
public IEnumerator RespawnHim(GameObject Him, Vector3 StartPos, float Time)
{
foreach(GameObject WhoIsIt in Guys)
{
if (WhoIsIt.name == Him.name)
{
if (WhoIsIt.transform.position == StartPos)
{
CloneHim = Instantiate(WhoIsIt) as GameObject;
GottenParent = WhoIsIt.transform.parent;
yield break;
}
}
}
if (CloneHim != null)
{
yield return new WaitForSeconds(Time);
GameObject Respawned = Instantiate(CloneHim) as GameObject;
Respawned.transform.position = StartPos;
Respawned.SetActive(true);
Respawned.transform.parent = GottenParent;
}
}
}```
I don't have the bit in there that makes sure theres only one instance
is that required for it to work?
only if you change scene, or have any way of potentially spawning more managers
alright.
if you change scene without DontDestroyOnLoad the Manager will forget everything that happened
gotcha.
my thinking here is that it'll store a copy of all enemies within it, and then the enemies can call the respawn script
tbh I need more food to properly analyze your code
just make sure you don't destroy the asset prefab, but the game object
and if you EVER use DestroyImmediate() for the Editor, make absolutely sure it does Not target an asset, because it will permanently delete it
in Play Mode, changes do not persists
if you're going to destroy object while in Editor Mode, you must use DestroyImmediate() instead of Destroy()
But if you target an asset, it will be poof gone
that'd be a big f
if I pass a GameObject to a function, and that gameobject is something like "transform.parent", that won't target the prefab, right?
I mean, I didn't even know prefabs were deletable like that, so
Destroy(gameObject) // ok
DestroyImmediate(gameObject_inTheScene) // ok
DestroyImmediate(gameObject_WhichIsAnAsset) <--- avoid
gotcha
so how can I reference to my RespawnAnyEnemy script as you have referenced to your LevelManager? if I try to do
RespawnAnyEnemy.RespawnHim() I just get an error currently
oh nvm
just didn't understand what vs was saying
kk
progress
fact
post the line of code
StartCoroutine(RespawnAnyEnemy.RespawnHim(transform.parent.parent.gameObject,StartPos,RespawnTime));
Better: Make a function which starts the coroutine from the manager
oo true
will do that
but it is still not able to get RespawnAnyEnemy for some reason
ah I thought static was needed somewhere. wasn't sure how that worked lol
SpawnManager.Instance.Respawn();
nice
some last words
when Managers manage multiple objects, it is normal to add them into a List<ofThoseObjects>
then you can use the List's Find function, along with some Lambda expressions, to interact with the desired object
lol
looks like progress to me ^^
Example:
private List<Brick> bricks;
public void DealDamageToBrick(string brickName, int ballDamageAmount)
{
var target = bricks.Find(x => x.Name == brickName);
target.TakeDamage(ballDamageAmount);
if (target.Health <= 0)
{
target.Die();
BrickDestroyed();
}
}
// in the Brick class
public void TakeDamage(int damageAmount)
{
Health -= damageAmount;
}
Gotta run
I'll check back later.
Have a nice day o/
you too