Unity3D optimize code - c#

I have 2 objects that do the same thing in terms of the behavior but the only thing that differs is the damage dealt. Right now i have 2 scripts that are a copy paste from each other. Same functions same everything. How can i make 1 script to optimize my work and not duplicate my stuff? Thanks.
using UnityEngine;
using System.Collections;
public class Script1 : MonoBehaviour {
private float attackDamage = 5;
void OnCollisionEnter (Collision item)
{
item.gameObject.GetComponent().Damage(attackDamage);
disableObs();
}
public void disableObs()
{
gameObject.SetActive(false);
}
}
Script 2 is the same thing. I just change the damage variable

Decorate your variable with:
[SerializeField] private int damage;
Now this is a private variable with exposure in the Inspector.
Concerning the workflow, you can drag as many of Script1 onto many objects, they will have their own Script1 set of actions. But the purpose is that you can have infantry with damage value as 1 and tank with damage as 100. All you need to do is set the value for the prefab. One place to set them all. Then you can instantiate the prefabs and they will use the given values.

You can change the damage variable to public and when you add the script to different objects set that variable via inspector. Or if you are doing that change by code on instantiation just call the setDamage(damage) function to the specific object:
gameobject.GetComponent<Script1>().setDamage(damage1);
gameobject2.GetComponent<Script1>().setDamage(damage2);

Related

Withdrawal of the amount of money via Unity Event

there is a problem with withdrawing the amount of money through Unity Event. Money is updated only for killing demons already on the stage at the time of the game launch, but not updated for killing demens created from the prefab. What could be the reason for this behavior? I attach the code and screenshots. Thank you in advance.
Demon
[SerializeField] public UnityEventInt onChangeMoney;
void DeathProcess()
{
onChangeMoney.Invoke(deathCost);
...
}
PlayerObjectScript
[SerializeField] public UnityEventInt onChangeMoney;
public int money;
public void ChangeMoney(int delta)
{
money += delta;
onChangeMoney.Invoke(money);
}
PlayerMoneyController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerMoneyController : MonoBehaviour
{
public Text text;
void Start()
{
}
public void UpdateHandler(int money)
{
text.text = money.ToString();
}
void Update()
{
}
}
P.S. Sorry for my English, translated by a translator
What could be the reason for this behavior?
It's about references, when you set up your scene, your enemy can have a reference about your turret because both of them exist in the same scene, both are objects that will exist in runtime. But at the moment that you make your enemy a prefab, they will turn into an asset, and an asset can't (at least not without additional effort) know about runtime items, can't ref references to it because they don't have any guarantee that when you drag your enemy into scene your turret will exist and that this turret reference is the same turret that you have set when they exist in hierarchy only. This is the reason why only your objects that exist in the scene will keep the right behavior.
What you need to do is find a way to get turret reference, maybe store it in another place and find it in a setup phase when instantiating new enemies. You can also create an event calling layer that can be a scriptable object, and a scriptable object as prefabs are both assets, so they can safely reference each other. You can also change this event to code only and turn it into a static event. You have many approaches to solve it, good luck

Use script for GameObject creation / changes outside of runtime

I have a prefab, which is used multiple times in my game (it will not be instatiated during runtime, I only use the prefab by dragging it into scene view to build my level).
Inside this prefab are many locations, where a specific text is written. This text is unique for every prefab.
So, it is like:
InstanceA
TextA (multiple times inside the instance)
InstanceB
TextB (multiple times inside the instance)
and so on ...
At the moment, I see two options to do it:
I change every text manually in every instance. This would work, but is a little annoying, also I can see me making mistakes (like a misspelling or forget to change one of the texts).
I solve it by a script and change the texts in the Awake or Start method. This would work, but it must be done every time, I start the game.
What I would hope for would be a solution like 2) but not every time (because nothing changes, it es every time the same) but only once - so it is generated by a script but after that, it will be always there. Even when I'm not in runtime and just work in the scene view.
Do you know any approach to get something done like this?
Thank you!
If you wish to run a piece of code outside the run-time, you can use one of the following two options.
OnValidate method
OnValidate is triggered when your MonoBehaviour script is loaded or an inspector value has changed.
using UnityEngine;
public class TestScript : MonoBehaviour
{
private void OnValidate()
{
Debug.Log("test");
}
}
ExecuteInEditMode attribute
Adding ExecuteInEditMode to a MonoBehaviour class will make it function both during and outside the runtime. If you do not wish the script to function during the runtime, feel free to also use Application.isPlaying.
using UnityEngine;
[ExecuteInEditMode]
public class TestScript : MonoBehaviour
{
private void Update()
{
if (!Application.isPlaying)
Debug.Log("test");
}
}

How do I access other elements in my predefined structured object in C#/Unity?

I made a predefined object called FOOD. Inside is a GameObject that points to a PreFab GameObject. That GameObject is also attached to a collision script. My question is, how do I access other elements of my structure such as health, calories, etc, inside my collision script?
public class FOODS: ScriptableObject
{
public GameObject preFabFood;
//points to a prefab object that is attached to a collision script
int health;
int calories;
float height;
float width;
}
//this is the script that is attached to the GameObject preFabFood
void OnCollisionEnter2D(Collision2D c)
{
//how do I access elements such as height, weight, etc?
}
You have two problems.
1) The properties are private, meaning that they aren't visible to other classes. The default visibility on member fields is private.
2) The properties aren't in a script attached to your prefab.
Fixing #2 (as Soraphis) suggests is sort of the right thing to do, however, you would want to remove the public GameObject preFabFood; field from your FOODS class if you're attaching the FOODS class to the prefab instance (as you don't need to carry a copy of the instantiated object with the instantiation). Just keep this in mind as you adjust your code (the code you posted looks like one script, but I suspect its supposed to be more than one, and should in fact be at least 2).
As for fixing #1, you have to make them public or access them from the same script. If you directly attach your FOODS class to your instantiated prefab and that the FOODS class contains the OnCollisionEnter2D method, then everything's fine.
But as I suspect you've got two classes that you're showing here, you'll need to do this:
public int health;
public int calories;
public float height;
public float width;
And then use a GetComponent() call from to get the one script from the other. e.g.
FOODS foods = this.gameObject.GetComponent<FOODS>(); //gets the FOODS script attached to this game object.
//Functionally identical to GetComponent<FOODS>();
int hp = foods.health; //etc
Note that you should call GetComponent as little as possible, because it has a non-zero overhead, so caching the result as soon as possible (e.g. you could do it in Start() instead of every time at the top of OnCollisionEnter2D), but this is more of a general "be careful" flag, as over-use can cause frame rate issues.
My question is, how do I access other elements of my structure such as health, calories, etc, inside my collision script?
you can't. but you can solve it like this:
in your CollisionScript, change the GameObject PrefabFood to your scriptable object FOODS food
then in this class you can access things like so:
food.preFabFood // is the prefab
food.health // is the health variable
it seams like you're quite new to coding and i'd suggest learning the basics before, because this is basic programming and isn't really unity or even c# related.

Instantiate creates multiple clones in Unity

I have the following code:
public Rigidbody2D ball;
Vector2 sp = new Vector2(0f, 2.1f);
void Update() {
if (Input.GetKeyDown("w")) {
SpawnBall();
DestroyBall();
}
}
void SpawnBall()
{
Instantiate(ball, sp, transform.rotation);
}
void DestroyBall()
{
if (ball.transform.position.y >= -5.7f)
{
Destroy(ball);
}
}
and the code is supposed to generate a new ball every time when "w" is pressed, but for some reason it creates multiple clones and it crashes the engine. How can I create a single clone only?
And also the destroy method doesn't do anything, although it should remove the clone when it passes -5.7 on the y-axis.
Thanks in advance
Create a new script named "SpawnRigidbody" and copy and paste the below code.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnRigidbody : MonoBehaviour
{
public Rigidbody2D ball;
Vector2 sp = new Vector2(0f, 2.1f);
void Update()
{
if (Input.GetKeyDown("w")) {
SpawnBall();
}
}
void SpawnBall()
{
Debug.Log ("spawn");
GameObject go = Instantiate(ball, sp, transform.rotation).gameObject;
go.AddComponent<DestroyAfterPosition> ();
}
}
Now create another script named "DestroyAfterPosition" and copy and paste the below code.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DestroyAfterPosition : MonoBehaviour
{
void Update ()
{
if (transform.position.y <= -5.7f)
{
Destroy(gameObject);
}
}
}
Create an empty game object and attach the SpawnRigidbody and then assign your ball in the inspector.
Hope this help you.
There are a couple problems with your Destroy() statement. First, it is destroying ball which is the Rigidbody2D. You will instead want to destroy the gameobject attached to the ball:
Destroy(ball.gameObject);
Second, you are trying to destroy the ball immediately after it is created, but only if y>=-5.7f. I think instead what you are looking to do is continuously check if the ball is above that point and destroy it if it is. Since you are creating multiple balls, they will all need their own check, which means you need to create a script for the ball prefab and in the Update() check its position and Destroy(gameObject) as necessary.
I think you may want to declare ball as a GameObject at the beginning of your script as well, so you are instantiating a gameobject and not just a Rigidbody2D:
public GameObject ball;
Object.Instantiate creates a clone of the provided object, so each time it is called a new object is created. MonoBehaviour.Update is called every frame, which means it's called at least 30 times per second (usually 60 or more on a PC).
So 30+ times per second, your code checks if the w key is held down, and, if so:
A clone of ball is created
Destroy the original ball if it is beyond -5.7 on the y-axis.
Thus, you're creating 30+ ball clones per second, and none will be deleted (because the DestroyBall method only looks at the original ball, not the clones).
If you want to work with the cloned object, assign the result of Instantiate to a field:
ballClone = Instantiate(ball, sp, transform.rotation);
Then you can check if ballClone exists and skip the SpawnBall call if the clone already exists. You can also use ballClone in the DestroyBall method.
ryemoss' answer seems like it's
also important. This answer is just based on looking at your code and the public Unity docs; I don't have a lot of experience with Unity.
Your destroy call is being called once, and only immediately after creating the object.
So it's currently doing this...
User clicks button...
Create ball...
Check to see if the ball is a certain height. It's not, so ignore...
Ball drops down based on gravity or whatever force compels it.
... and is never deleted.
If you want the ball to be destroyed when it reaches a certain point, either...
Have SpawnBall return the game object, which you store and check / delete later in Update,
...or...
Create a script that just checks the object's transform.position and blows up if it's where it needs to be. Attach that to the prefab of the ball you're creating a duplicate of.

Spawning Player at certain Point in Unity

I am making a small 2d click'n'point in Unity, and what I want to do is: I want to move towards the door and when my Player steps on a game Object with an attached SceneSwitcher Script he shall go through the door, into another scene. That works fine so far. Now I don't want him to appear in the middle of the room, but on the door, where he entered the room.
using UnityEngine;
using System.Collections;
using PixelCrushers.DialogueSystem;
public class ScenSwitcher : MonoBehaviour {
public string SceneName = "";
void OnTriggerEnter2D(Collider2D other) {
SwitchScene();
}
void SwitchScene(){
LevelManager levelManager = DialogueManager.Instance.GetComponent<LevelManager>();
levelManager.LoadLevel (SceneName);
changePosition ();
Debug.Log ("Scene Wechseln nach: " + SceneName);
}
void changePosition(){
GameObject player = GameObject.Find("Player");
player.transform.position = new Vector3(12,12,0);
}
}
That is my code, it does change Scenes, but not change the position. I would appreciate any help :)
On your ChangePosition() method you are passing hardcoded values to player position and it will assume always (12,12,0) on your scene space.
You need to define a spawn manager where you will get dynamically witch spawn point in your scene you want to use.
edited:
1: Try to create a singleton GameManager ( you can find singleton pattern examples here ) (IMPORTANT: Add DontDestroyOnLoad on your GameManager Awake).
2: In your GameManager define a Vector3 NextPosition property or something like this.
3: Declare a public Vector3 Destination on your "teleport" script to set it per teleport on inspector/editor.
4: Before this line levelManager.LoadLevel (SceneName) of code set GameManager.NextPosition = this.Destination;
5: If you are not persisting your character between scenes just call on one of hes behaviours Awake() or, if he persists create a method void OnLevelWasLoaded(int level) and chage players position setting GameManager.NextPosition ( wisely testing if it is valid for the current level before ;) ).
I cant try or do better coding now because I don't have access to unity editor so I hope it helps at last start a good research to solve your problem =/.
I would think the loadLevel function destroys the current script so changePosition does not get executed? I could be wrong.
Even if it is getting executed, there is a good chance it is executed before the level load and the properties for the next scene override where it got moved to.
I forget the exact syntax but look into getting GameObjects to not be destroyed on scene change.
EDIT
Object.DontDestroyOnLoad

Categories