I'm trying to create a statue where you can refill your mana. Here is my code:
public int mana = 5;
void Start()
{
mana = 5;
}
void Update()
{
if(mana < 0)
{
mana = 0;
}
if(mana > 5)
{
mana = 5;
}
}
public void refillMana()
{
mana = 5;
if(mana == 5)
{
Debug.Log("Mana is refilled");
}
}
I have this in a script and then a seperate script calls refillMana() using an event but my mana int doesn't update in the inspector and I can't use my spells. But the console still says "Mana is refilled".
I tried using the statue by activating the UnityEvent and I expected the inspector to say that my mana was 5 but it still said 0 and I couldn't use my spells.
Note: in the past it did work but only when I hadn't entered a load zone, I tried to fix that but then I got this problem.
Related
I am making a script for a unity project to destroy an instantiated clone if it collides with another clone, but since the Game Object (the clone) is declared in the start method and I cannot put it at the top I need to figure out how to destroy the clone if it collides with something else.
This is the error I get if I put it on top:
A field initializer cannot reference the non-static field, method, or property
Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class topbigspike : MonoBehaviour
{
public GameObject Flame;
// Start is called before the first frame update
void Start()
{
int a = 30;
int i = 0;
while (0 < a)
{
a--;
i++;
GameObject FlameClone = Instantiate(Flame);
FlameClone.transform.position = new Vector3(Random.Range(10, 2000), -3, 0);
}
}
void OnCollisionEnter2D(Collision2D col)
{
Destroy(FlameClone);
}
}
As the error message says, you cannot use Flame as field initializer. But you can still declare FlameClone as field (at the top, as you say) ) and initialize it in the Start method:
public class topbigspike : MonoBehaviour
{
public GameObject Flame;
public GameObject FlameClone; // <=== Declare here, as class field.
// Start is called before the first frame update
void Start()
{
int a = 30;
int i = 0;
while (0 < a)
{
a--;
i++;
FlameClone = Instantiate(Flame); // <=== Initialize here.
FlameClone.transform.position = new Vector3(Random.Range(10, 2000), -3, 0);
}
}
void OnCollisionEnter2D(Collision2D col)
{
Destroy(FlameClone);
}
}
As already mentioned you need to store it a field in order to access it from other methods.
However, seeing a while loop there instantiating multiple (30) instances a single field isn't enough anyway except you really want to only destroy the last created instance.
It should probably rather be e.g.
public class topbigspike : MonoBehaviour
{
public int amount = 30;
public GameObject Flame;
private GameObject[] flameInstances;
void Start()
{
flameInstances = new GameObject[amount];
for(var i = 0; i < amount; i++)
{
var flameInstance = Instantiate(Flame);
flameInsance.transform.position = new Vector3(Random.Range(10, 2000), -3, 0);
flameInstances[i] = flameInstance;
}
}
void OnCollisionEnter2D(Collision2D col)
{
foreach(var flameInstance in flameInstances)
{
Destroy(flameInstance);
}
}
}
I have such a script:
public class SystemLives : MonoBehaviour
{
public int lives;
public int maxLives;
public Image[] Live;
public Sprite FullHearts;
public Sprite EmptyHearts;
public void setLives(int time)
{
lives += Mathf.RoundToInt(Time.deltaTime * time);
if (lives > maxLives)
{
lives = maxLives;
}
for (int i = 0; i < Live.Length; i++)
{
if (i < lives)
{
Live[i].sprite = FullHearts;
}
else
{
Live[i].sprite = EmptyHearts;
}
}
}
public void TakeHit(int damage)
{
lives -= damage;
if (lives <= 0)
{
Debug.Log("Игра окончена");
}
for (int i = 0; i < Live.Length; i++)
{
if (i < Mathf.RoundToInt(lives))
{
Live[i].sprite = FullHearts;
}
else
{
Live[i].sprite = EmptyHearts;
}
}
}
n which there are functions for subtracting and adding life, I hung this script on an empty element, I call it like this:
public GameObject ScriptLives;
private SystemLives systemLives;
public int times=1;
public void Btn()
{
foreach (var s in strings)
{
if (s.Compare() == true)
{
b++;
}
else
{
prob++;
}
}
systemLives = ScriptLives.GetComponent<SystemLives>();
systemLives.setLives(times);
systemLives = ScriptLives.GetComponent<SystemLives>();
systemLives.TakeHit(prob);
Debug.Log($"{b}");
}
I call it like this: It turns out to take lives, but for some reason it is not added. maybe the problem is in the first script? please tell me what the reason may be and how it can be fixed?
You're calling
lives += Mathf.RoundToInt(Time.deltaTime * time);
but Time.deltaTime is the time between frame draws. Even an awful game might still have 10 fps, which means Time.deltaTime is 0.1 seconds. You're passing 1 in as the argument to that function, so (0.1*1) = 0.1.
Then you take 0.1, round it to an integer, so it rounds to 0. Then you increment lives by zero.
I really can't tell what the goal is here with the time dependency on adding lives, so unfortunately I've got no suggestions, but hopefully this helps.
An easy way to ask your question is to use InvokeRepeating, which works as follows:
public void Start()
{
InvokeRepeating(nameof(AddLife), 0f, 2f);
}
public void AddLife() // add 1 life every 2 sec
{
life = Mathf.Min(++life, maxLives);
Debug.Log("Life: "+life);
}
If you want to be time dependent, change the life adjustment algorithm to Time.deltaTime to Time.time.
public void Update()
{
SetLife(3f); // set life every 3 sec for e.g.
}
public void setLives(float repeatTime)
{
life = (int) (Time.time / repeatTime);
}
Im working on a AI combat system where each AI has a secondary collider with "Trigger" enabled. here is my script so far
public float health = 100;
public int isrunning = 1;
public GameObject currenttarget;
public int attackspeed;
public int damage;
public int newdamage = 0;
void Start()
{
StartCoroutine(DoDamage());
}
public void TakeDamage(float x)
{
this.health = this.health - x;
}
public IEnumerator DoDamage()
{
isrunning = 0;
yield return new WaitForSeconds(attackspeed);
Debug.Log("loop");
newdamage = damage;
isrunning = 1;
}
private void OnTriggerStay(Collider other)
{
if ( other.gameObject.CompareTag("AI"))
{
other.GetComponent<Framework>().TakeDamage(newdamage);
newdamage = 0;
}
}
private void Update()
{
if (isrunning==1)
{
StartCoroutine(DoDamage());
}
}
// Update is called once per frame
}
When I place three objects with this script where there damage is set to 5 and attack rate to 1, The result that I want out of this would be: A.100 B.100 C.100 (1 Second) A.80 B.80 C.80 However what I find is that the other.GetComponent().TakeDamage is only applying to one object at a time rather then being applied to the other two objects as I want. is this how the OnTriggerStay should be working? and if so are there any workarounds for this?
In my way, I suggest you to use a List of object, whenever the eneny attack(OnTriggerEnter) the player, you can push them to the list, then use the foreach loop in the Update Func to do some Function like take damage,... , after the enemy stop attack(OnTriggerExit) you can Pop its out of the List.
I have made a game manager making sure my data gets passed on from the first scene on to the next scene. Within the game manager, I have added certain scripts to ensure it gets passed. As you can see in the picture underneath.
The problem is that the score adds up at the first level, let's say I have 5 points. I go to level 2 and the UI shows my score as 0 (even tho I have nothing put as text within the score text) I kill 1 monster and the UI shows 6. So how can I put the UI to be showing it at all times? (Constant refresh?)
The second problem is that while the score manager does work. The health script cancels everything out when switching levels.
The user starts with 10 health. Takes damage in the first scene, but in the second scene, the user still has 10 health for some reason?
Game Manager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour {
public ActionButton PopupPrefab;
private ActionButton currentlySpawnedPopup;
public static GameManager instance = null;
void Awake () {
if (instance == null) {
instance = this;
} else if (instance != this) {
Destroy (gameObject);
}
Application.targetFrameRate = 60;
}
void Update () {
//if (Input.GetKeyDown(KeyCode.R)) {
// RestartScene ();
//}
}
public void InvokeRestartScene (float time) {
Invoke ("RestartScene", time);
}
public void RestartScene () {
SceneManager.LoadScene (0);
}
public void SpawnPopup (Vector2 position) {
DespawnPopup ();
currentlySpawnedPopup = Instantiate (PopupPrefab, position, Quaternion.identity) as ActionButton;
}
public void DespawnPopup () {
if (currentlySpawnedPopup != null) {
currentlySpawnedPopup.DestroySelf();
currentlySpawnedPopup = null;
}
}
public void FadePopup () {
if (currentlySpawnedPopup != null) {
currentlySpawnedPopup.FadeMe ();
currentlySpawnedPopup = null;
}
}
}
Score Manager
using UnityEngine;
using System;
using System.Collections;
public class ScoreManager : MonoBehaviour
{
public static ScoreManager Instance { get; private set; }
public int Score { get; private set; }
public int HighScore { get; private set; }
public bool HasNewHighScore { get; private set; }
public static event Action<int> ScoreUpdated = delegate {};
public static event Action<int> HighscoreUpdated = delegate {};
private const string HIGHSCORE = "HIGHSCORE";
// key name to store high score in PlayerPrefs
void Awake()
{
if (Instance)
{
DestroyImmediate(gameObject);
}
else
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
}
void Start()
{
Reset();
}
public void Reset()
{
// Initialize score
Score = 0;
// Initialize highscore
HighScore = PlayerPrefs.GetInt(HIGHSCORE, 0);
HasNewHighScore = false;
}
public void AddScore(int amount)
{
Score += amount;
// Fire event
ScoreUpdated(Score);
if (Score > HighScore)
{
UpdateHighScore(Score);
HasNewHighScore = true;
}
else
{
HasNewHighScore = false;
}
}
public void UpdateHighScore(int newHighScore)
{
// Update highscore if player has made a new one
if (newHighScore > HighScore)
{
HighScore = newHighScore;
PlayerPrefs.SetInt(HIGHSCORE, HighScore);
HighscoreUpdated(HighScore);
}
}
}
Health Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class Health : MonoBehaviour {
public UnityEvent OnTakeDamageEvent;
public UnityEvent OnTakeHealEvent;
public UnityEvent OnDeathEvent;
[Header ("Max/Starting Health")]
public int maxHealth;
[Header ("Current Health")]
public int health;
[Header ("IsDeathOrNot")]
public bool dead = false;
[Header ("Invincible")]
public bool invincible = false;
public bool becomeInvincibleOnHit = false;
public float invincibleTimeOnHit = 1f;
private float invincibleTimer = 0f;
[Header ("Perform Dead Events after x time")]
public float DieEventsAfterTime = 1f;
void Start () {
health = maxHealth;
}
void Update () {
if (invincibleTimer > 0f) {
invincibleTimer -= Time.deltaTime;
if (invincibleTimer <= 0f) {
if (invincible)
invincible = false;
}
}
}
public bool TakeDamage (int amount) {
if (dead || invincible)
return false;
health = Mathf.Max (0, health - amount);
if (OnTakeDamageEvent != null)
OnTakeDamageEvent.Invoke();
if (health <= 0) {
Die ();
} else {
if (becomeInvincibleOnHit) {
invincible = true;
invincibleTimer = invincibleTimeOnHit;
}
}
return true;
}
public bool TakeHeal (int amount) {
if (dead || health == maxHealth)
return false;
health = Mathf.Min (maxHealth, health + amount);
if (OnTakeHealEvent != null)
OnTakeHealEvent.Invoke();
return true;
}
public void Die () {
dead = true;
if (CameraShaker.instance != null) {
CameraShaker.instance.InitShake(0.2f, 1f);
}
StartCoroutine (DeathEventsRoutine (DieEventsAfterTime));
}
IEnumerator DeathEventsRoutine (float time) {
yield return new WaitForSeconds (time);
if (OnDeathEvent != null)
OnDeathEvent.Invoke();
}
public void SetUIHealthBar () {
if (UIHeartsHealthBar.instance != null) {
UIHeartsHealthBar.instance.SetHearts (health);
}
}
}
I have thought of adding the following script on to my Health Script
But then I get the following error messages:
" Cannot implicitly convert type 'int' to 'bool'"
"The left-hand side of an assignment must be a variable, property or indexer"
void Awake()
{
if (health)
{
DestroyImmediate(gameObject);
}
else
{
(int)health = this;
DontDestroyOnLoad(gameObject);
}
}
The problem is that the score adds up at the first level, let's say I have 5 points. I go to level 2 and the UI shows my score as 0 (even tho I have nothing put as text within the score text) I kill 1 monster and the UI shows 6. So how can I put the UI to be showing it at all times? (Constant refresh?)
You can make one of the scripts set the UI text score when the scene is loaded.
void Start(){
// Loads the scoreText on start
scoreText.text = yourCurrentScore.ToString();
// Will work unless it has a "DontDestroyOnLoad" applied to it
}
The second problem is that while the score manager does work. The
health script cancels everything out when switching levels. The user
starts with 10 health. Takes damage in the first scene, but in the
second scene, the user still has 10 health for some reason?
In your health script, you had this:
void Start () {
health = maxHealth;
}
This resets your health everytime the scene loads and starts (Aka when you load to the next level). This causes the issue.
" Cannot implicitly convert type 'int' to 'bool'"
if (health)
The () for the if statement should be a condition (a question).
For example, doing health < 0 is valid since its saying "Is health less than 0?"
Doing health is not, since its just saying "10" (or some number).
"The left-hand side of an assignment must be a variable, property or
indexer"
(int)health = this;
If you wanted to change the value of health, just do health = 10 or health = some_Variable_That_Is_An_Integer
after the level finishes the win screen is shown. which has two buttons continue and menu.i have managed to deactivate the buttons and only keep the 1st level button unlocked but cant get level 2 button to unlock when i clear level 1. also i want the continue button to jump to level 2 after it is shown in the next scene when completing level 1. this game is breakout style one and these are the problems that i am not able to solve. i have attached the respective scripts that i thought were necessary. if u want the others plz ask for them in the comments.a complete list of all scripts is at the end.i would really appreciate some help.i will definitely try to explain my problem a little more if u ask for them . so plz do check this question again later to checkout the changes.
the script below is attached to the level 1:-
{
[SerializeField] int breakableBlocks; // Serialized for debugging purposes
SceneLoader sceneloader;
private void Start()
{
sceneloader = FindObjectOfType<SceneLoader>();
}
public void CountBreakableBlocks()
{
breakableBlocks++;
}
public void BlockDestroyed()
{
breakableBlocks--;
if (breakableBlocks <= 0)
{
GetComponent<LevelSelector>().levelunlocked =
sceneloader.LoadWinScreen();
}
}
}
The script below is attached to level selector:-
{
public Button[] levelButtons;
public int levelunlocked = 1;
private void Start()
{
int levelReached = PlayerPrefs.GetInt("levelReached", levelunlocked);
for (int i = 0; i < levelButtons.Length; i++)
{
if (i + 1 > levelReached)
{
levelButtons[i].interactable = false;
}
}
}
}
Scene loader script:-
public class SceneLoader : MonoBehaviour {
public void LoadNextScene()
{
int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
SceneManager.LoadScene(currentSceneIndex + 1);
}
public void LoadStartScene()
{
SceneManager.LoadScene(0);
}
public void LoadLevelSelectScreen()
{
SceneManager.LoadScene(1);
}
public void LoadWinScreen()
{
SceneManager.LoadScene(5);
}
public void LoadGameOverScreen()
{
SceneManager.LoadScene(6);
}
public void QuitGame()
{
Application.Quit();
}
public void Level1()
{
SceneManager.LoadScene(2);
}
public void Leve2()
{
SceneManager.LoadScene(3);
}
}
this is the build setting:-
build settings
The Scripts that i have :-
1.Ball
2.Block
3.Level
4.LevelSelector
5.LoseCollider
6.Paddle
7.SceneLoader
I think your issue stems from not updating your "levelReached" player prefs value before leaving your level 1 scene.
Within your posted level 1 script:
public void BlockDestroyed()
{
breakableBlocks--;
if (breakableBlocks <= 0)
{
GetComponent<LevelSelector>().levelunlocked =
sceneloader.LoadWinScreen();
}
}
The following line should be throwing an error as you're LoadWinScreen function returns void:
GetComponent<LevelSelector>().levelunlocked =
sceneloader.LoadWinScreen();
Try changing that section of code to the following:
if (breakableBlocks <= 0)
{
PlayerPrefs.SetInt("levelReached", 2);
sceneloader.LoadWinScreen();
}
Note in the above example I have assumed you have a separate script running game logic for each level as I am not using a variable to set the new PlayerPrefs "levelReached" value.
I would recommend having a GameManager script that is present in each scene and tracks the level you are currently on, which would allow you to do the following:
if (breakableBlocks <= 0)
{
if(PlayerPrefs.GetInt("levelReached") < GameManager.currentLevel + 1)
PlayerPrefs.SetInt("levelReached", GameManager.currentLevel + 1);
sceneloader.LoadWinScreen();
}
This requires some separate logic for carrying game state across scenes and there are several ways to go about this (see examples and relevant stackoverflow link below):
Singleton design pattern using the Unity DontDestroyOnLoad function
ScriptableObjects to store and retrieve data at level start
PlayerPrefs to store and retrieve data at level start
Unity - pass data between scenes