I'm attempting to allow the players to open the door once they have located the key
The 'hasKey' value is currently managing if the players has the key with either true or false. i now need to know how to use this 'hasKey' boolean in another script; i've been trying for hours and getting no where so i'll post my code below and maybe someone knows whats going on, thanks in advance!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class Detection : MonoBehaviour {
public GameObject objectToEnable;
public static bool Enabled = false;
public bool hasKey = false;
public DoorOpener _DoorOpener;
private void Update()
{
Debug.Log(hasKey);
if (Enabled)
{
objectToEnable.SetActive(true);
}
}
void OnMouseEnter()
{
Debug.Log("Enter");
}
void OnMouseExit()
{
Debug.Log("Exit");
}
void OnMouseUp()
{
Enabled = true;
hasKey = true;
Debug.Log("Pressed");
}
}
public class DoorOpener : MonoBehaviour
{
Animator animator;
bool JailDoorOpen;
public Detection _Detection;
void Start()
{
JailDoorOpen = false;
animator = GetComponent<Animator>();
}
void OnTriggerEnter(Collider JailDoorO)
{
if ((JailDoorO.gameObject.tag == "Player") && (_Detection.hasKey == true))
{
Debug.Log("Open Door");
JailDoorOpen = true;
jDoors("Open");
}
}
void jDoors (string direction)
{
animator.SetTrigger(direction);
}
}
enter code here
In your second script you have declared:
public Detection _Detection;
but you have not said what _Detection is assigned to. So it is just a blank instance of your Detection script. You need to reference the script that is attached to the specific object you are looking for.
For example if Detection and DoorOpener are both on the same gameobject you would do.
_Detection = gameObject.getComponent<Detection>();
or otherwise you could do something like...
_Detection = GameObject.FindWithTag('TagOfObjWithDetScript').getComponent<Detection>();
now the value of haskey in DoorOpener matches the value of haskey in the specific instance of the Detection script you are using.
If this is a local multiplayer game, you could store the boolean on the door object. You could also store it in a KeyManager and access it through a static variable.
If this is a network multiplayer game, you're going to have to store the variable in a manager somewhere and update each client when the state of that boolean changes.
Related
So I'm new to C# I somewhat know Python I couldn't understand how functions work I tried doing something like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class collisiondetectorleft : MonoBehaviour
{
public class Triggerdetecting()
{
public void OnTriggerStay(Collider other)
{
if (other.attachedRigidbody)
other.attachedRigidbody.AddForce((Vector3.up * 10);
}
}
void FixedUpdate()
{
if (Input.GetKeyDown("space"))
{
//I'm so lost
Triggerdetecting objTriggerdetecting = new Triggerdetecting();
}
}
}
I'm trying to create some sort of hitbox by detecting trigger if a button pressed and meets the condition make the object more faster. I tried few ways to call function non of them worked. Thank you for your time. If you unable to understand what I meant you can ask me I'll try to explain in other ways.
Want something like this:
def detection():
if OnTriggerStay == True:
moveobject up
if Input.GetKeyDown("space")) == True:
detection()
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class collisiondetectorleft : MonoBehaviour
{
//create bool to set it true or false while inside the object
private bool _canHit;
private void Update()
{
if (_canHit)
{
if (Input.GetKeyDown(KeyCode.A))
{
_canHit = false;
//task
}
}
}
//set _canHit true if object enters trigger
private void OnTriggerEnter(Collider other)
{
if (other.attachedRigidbody)
_canHit = true;
}
//set _canHit false if object exits trigger
private void OnTriggerExit(Collider other)
{
if (other.attachedRigidbody)
_canHit = false;
}
}
I was able to detect and log while true but I didn't figured how to react to object that triggers
I would go the same route as you did in your answer attempt.
The last bit missing in order to b able to actually use the according object is simple: Instead of a bool rather directly store the object
public class collisiondetectorleft : MonoBehaviour
{
private Collider _currenTarget;
private void Update()
{
if (_currenTarget && Input.GetKeyDown(KeyCode.A))
{
Debug.Log($"I am interacting with {_currentTarget}");
_currenTarget = null;
}
}
private void OnTriggerEnter(Collider other)
{
if (!other.attachedRigidbody) return;
_currenTarget = other;
}
private void OnTriggerExit(Collider other)
{
if (other != _currenTarget) return;
_currenTarget = null;
}
}
I am making Replay logic for my game, where when I click replay I got to the Main Page. The problem I am facing is that after clicking Play on the game after coming from Replay, the Zombie character in my game is not showing up. The game is running without the player. I am posting the script, check the Replay function which is attached to Replay button in the game.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
public class GameManager : MonoBehaviour
{
public static GameManager instance = null;
private bool playerActive = false;
private bool gameOver = false;
private bool gameStarted = false;
private GameObject newZombie;
[SerializeField] private GameObject mainMenu; //contains main menu content
[SerializeField] private GameObject endGame; //contains game over content
[SerializeField] private GameObject zombie;
public bool PlayerActive{
get{
return playerActive;
}
}
public bool GameOver{
get{
return gameOver;
}
}
public bool GameStarted{
get{
return gameStarted;
}
}
void Awake()
{
if(instance == null){
instance = this;
}else if(instance != this){
Destroy(gameObject);
}
Assert.IsNotNull(mainMenu);
Assert.IsNotNull(endGame);
DontDestroyOnLoad(gameObject);
}
// Start is called before the first frame update
void Start()
{
endGame.SetActive(false);
mainMenu.SetActive(true);
}
// Update is called once per frame
void Update()
{
}
public void PlayerCollided()
{
gameOver = true;
endGame.SetActive(true);
mainMenu.SetActive(false);
DontDestroyOnLoad(gameObject);
}
public void PlayerStartedGame()
{
playerActive = true;
}
public void EnterGame()
{
endGame.SetActive(false);
mainMenu.SetActive(false);
gameStarted = true;
}
public void Replay()
{
endGame.SetActive(false);
mainMenu.SetActive(true);
gameOver = false;
newZombie = Instantiate(zombie) as GameObject;
}
There are a lot of assumptions we have to make based on the information you gave.
Try instantiating the zombie on a specific location. You're using Instantiate(gameObject), but there's a different variant to the Instantiate method which also takes a Vector3 position to spawn the object as a second argument.
If that doesn't work, please reply with answers to the following questions:
Does the zombie spawn at all (is it in the hierarchy)
Which methods exactly do the buttons invoke, for example:
[1]: https://i.stack.imgur.com/9xbgy.png
You're using a singleton pattern but you don't change scenes in this class. Are you changing scenes in any of your scripts? If yes, you will have to consider looking into them because every script that is persisting between scenes with the DontDestroyOnLoad() method could potentially interfere with your player.
Vlad
I've been stuck at this for a while. What I want is for my outline object to be instantiated at the location of my bronze Base game object and for it to destroy when the bronze base is no longer the closest to the player.
I'm willing to completely restart my bronze script if it means I can make this easier.
Thanks in advance!
Find Closest Bronze Script
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;
public class FindBronze : MonoBehaviour
{
void Update()
{
FindClosestBronze();
}
void FindClosestBronze()
{
float distanceToClosestBronze = Mathf.Infinity;
Bronze closestBronze = null;
Bronze[] allBronze = GameObject.FindObjectsOfType<Bronze>();
foreach (Bronze currentBronze in allBronze)
{
float distanceToBronze = (currentBronze.transform.position - this.transform.position).sqrMagnitude;
if (distanceToBronze < distanceToClosestBronze)
{
distanceToClosestBronze = distanceToBronze;
closestBronze = currentBronze;
}
if (distanceToBronze > distanceToClosestBronze)
{
closestBronze.GetComponent<Bronze>().notSelected();
}
closestBronze.GetComponent<Bronze>().Selected();
}
}
}
Bronze (includes outline) script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bronze : MonoBehaviour
{
public bool isSelected = false;
public Animator anim;
[SerializeField]
public GameObject selectedBox;
public GameObject bronzeBase;
private GameObject clone;
// Update is called once per frame
void Awake()
{
clone = (GameObject)Instantiate(selectedBox, bronzeBase.transform);
}
public void Selected()
{
if (!isSelected)
{
clone = (GameObject)Instantiate(selectedBox, bronzeBase.transform);
isSelected = true;
}
else
{
Destroy(clone);
isSelected = false;
}
}
public void notSelected()
{
Destroy(selectedBox);
}
}
In the Bronze in notSelected you are destroying the prefab selectBox!
You probably rather wanted to destroy the clone instance.
Anyway I would suggest a few things that I would do different
Instead of Instantiate and Destroy all the time rather only use SetActive
Instead of using FindObjectOfType in Update store them in a HashSet event driven: Each Bronze instance registers and unregisters itself
Depends on personal taste but I would use Linq to find the closest instance
This could look somewhat like
public class Bronze : MonoBehaviour
{
// Every instance of this component registers and unregisters itself here
public static readonly HashSet<Bronze> Instances = new HashSet<Bronze>();
[Header("References")]
public Animator anim;
[SerializeField] private GameObject selectedBox;
[SerializeField] private GameObject bronzeBase;
[Header("Debugging")]
[SerializeField] bool _isSelected = false;
private GameObject clone;
// Have a property for the selection
public bool IsSelected
{
// when something reads this property return _isSelected
get => _isSelected;
// This is executed everytime someone changes the value of IsSelected
set
{
if(_isSelected == value) return;
_isSelected = value;
clone.SetActive(_isSelected);
}
}
// Update is called once per frame
void Awake()
{
// Instantiate already returns the type of the given prefab
clone = Instantiate(selectedBox, bronzeBase.transform);
// Register yourself to the alive instances
Instances.Add(this);
}
private void OnDestroy ()
{
// Remove yourself from the Instances
Instances.Remove(this);
}
}
And then use it
using System.Linq;
public class FindBronze : MonoBehaviour
{
private Bronze currentSelected;
private void Update()
{
UpdateClosestBronze();
}
void UpdateClosestBronze()
{
if(Bronze.Instances.Count ==0) return;
// This takes the instances
// orders them by distance ascending using the sqrMagnitude
// of the vector between the Instance and you
// sqrMagnitude is more efficient than Vector3.Distance when you only need to compare instead of the actual distance
// then finally it takes the first item
var newClosest = Bronze.Instances.OrderBy(b => (b.transform.position - transform.position).sqrMagnitude).First();
// skip if the result is the same as last time
if(newClosest == currentSelected) return;
// otherwise first deselect the current selection if there is one
if(currentSelected)
{
currentSelected.IsSelected = false;
}
// Then set the new selection
currentSelected = newSelected;
currentSelected.IsSelected = true;
}
}
I have multiple enemies set up in a level, all using the same behaviour and animator scripts. When I hit or kill one of them, all of them get hit or killed. I need them to function as separate instances.
I have tried referencing only an instance of the script:
private GoblinBehaviour goblin;
goblin = GetComponent<GoblinBehaviour>();
goblin.IncrementHits(1);
But that doesn't work. An error arises that says that script can't be accessed with an instance and needs a type instead.
code for hit detection script:
public class RangedDetection : MonoBehaviour
{
private GoblinBehaviour goblin;
void OnTriggerEnter(Collider other)
{
//on colliding destroy rocks after its life time
Destroy(gameObject, rockLifeTime);
if (other.gameObject.tag.Equals("Enemy"))
//if (other.tag == "Enemy")
{
Destroy(other.gameObject);
}
else if (other.gameObject.tag.Equals("Goblin"))//goblin should play
death animation
{
goblin = GetComponent<GoblinBehaviour>();
goblin.IncrementHits(1);
GetComponent<BoxCollider>().enabled = false; //Removing hit
collider so it only hits target once.
}
}
}
Simplified Code for goblin script:
public class GoblinBehaviour : MonoBehaviour
{
Transform player;
public static bool isDead;
public static bool isPunching;
public static bool isHit;
public GameObject particles;
public Animator anim;
public void IncrementHits(int hitCount)
{
isHit = true;
hits += hitCount;
if (hits >= 2) isDead = true;
}
void Die()
{
Instantiate(particles, transform.position, transform.rotation);
Destroy(gameObject);
}
void updateAnim()
{
anim.SetBool("Punch_b", isPunching);
anim.SetBool("hit", isHit);
}
}
Things should animate and act separately, I'm not sure how to reference only the current instance of the script.
While your code is incomplete and the problem cannot be said for sure, it looks like you are using statics improperly.
Static properties are instance analagous. In other words, all of your goblin instances share any static properties (isPunching, isHit, isDead, etc.). Making these values static allows you to reference them directly without having to obtain the instance you're affecting, but results in you updating all goblins at once.
Your solution will involve removing the static modifier from your GoblinBehaviour properties unless the properties are meant to be shared across all instances.
I want to have the player start from a start position at first. Then start from a checkpoint after they've completed level one. I've tried different iterations of this script, it is supposed to use a bool check if the player has passed through a trigger, and if they have their position will be equal to the checkpoint at Start. It has to remember the bool when the game is turned off and on again.
Right now the player always starts from the checkpoint.
I tried making my own boolean with playprefs but ended up using BoolPrefs, which still didn't work.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public Rigidbody player;
public Transform startPoint;
public Transform checkPoint;
private void Start()
{
if (PlayerPrefsX.GetBool("level01Complete", false))
{
player.transform.position = startPoint.position;
}
if (PlayerPrefsX.GetBool("level01Complete", true))
{
player.transform.position = checkPoint.position;
}
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "HubTrigger")
{
PlayerPrefsX.SetBool("level01Complete", true);
}
else
{
PlayerPrefsX.SetBool("level01Complete", false);
}
}
}
Here is the BoolPrefs script I'm using currently.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerPrefsX
{
public static void SetBool(string name, bool booleanValue)
{
PlayerPrefs.SetInt(name, booleanValue ? 1 : 0);
}
public static bool GetBool(string name)
{
return PlayerPrefs.GetInt(name) == 1 ? true : false;
}
public static bool GetBool(string name, bool defaultValue)
{
if (PlayerPrefs.HasKey(name))
{
return GetBool(name);
}
return defaultValue;
}
}
I am new to using PlayerPrefs so there may just be something obvious that I'm overlooking.
HubTrigger is the trigger you pass through after completing level one. So it should be that the first time you play you start from one spot, then once you pass through hubtrigger, you've completed level one and from then on you start from the checkpoint when you load the game.
PlayerPrefsX looks fine. It just seems like you're using it incorrectly. You should check if (PlayerPrefsX.GetBool("level01Complete", false)), and if that's true, then set the checkpoint position, and set the start position otherwise:
private void Start()
{
if (PlayerPrefsX.GetBool("level01Complete", false))
{
player.transform.position = checkPoint.position;
}
else
{
player.transform.position = startPoint.position;
}
}
For OnTriggerEnter, you really only should set level01Complete there in the event you beat level 1:
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "HubTrigger")
{
PlayerPrefsX.SetBool("level01Complete", true);
}
}