I'm trying to have it so when I pick up an object, in this case a gun, it tags a bool, only problem is the gun pick up and the gun shoot scripts are two different scripts. So currently I'm trying to get the pickup script to effect the shoot script. I got it to change with the pickup script but can't get the pickup script to change the shoot script, here's the bit of code in question, any help would be greatly appreciated. If the full code of both scripts are needed id be happy to oblige, I just didn't want to force people to sort through 120 lines of code right off the bat.
public float damage = 10f;
public float range = 100f;
public float fireRate = 15f;
public float Impact = 40f;
public Camera fpsCam;
public bool pickedUp = false;
public ParticleSystem muzzleFlash;
public GameObject impactEffect;
private float nextTimeToFire = 0f;
private PickupScript myScript;
// Start is called before the first frame update
void Start()
{
if (gameObject.GetComponent<PickupScript>().pickedUp = true) ;
setbool pickedUp = true;
}
Note: Your if conditional lacks one "=", to be a comparison, not an asignment.
If that was a typo error, read further:
If I understood correctly, you only need to know that you can reference those scripts to each other.
Seems that you already know that cause you are using PickupScript as a variable, so I'll try to refresh the ways to reference that kind of stuff, so maybe it helps.
Scripts are classes, like GameObject is also a class. So when you make a GameObject variable public and assign from Editor the corresponding GameObject, you are stablishing a reference.
You could also do that finding on your game the script to reference, by TAG name for example.
In your case you got 2 classes, PickUpScript and GunShot.
If they are part of the same GameObject, you can assign them from Editor, if not, use FindGameObjectsWithTag. Once you have the reference in each script, you can assign the value bidirectionally.
public class PickupScript : MonoBehaviour
{
public bool pickUpBool = false;
public Shotgun shotgun = false;
}
public class Shotgun : MonoBehaviour
{
public bool shotgunBool = false;
public PickupScript pickupScript = false;
private void Start()
{
if (gameObject.GetComponent<PickupScript>().pickUpBool == true)
{
shotgunBool = true;
}
}
}
Related
I'm currently making a 3D endless runner game with the choice to select different skins for the player. Everything's going smoothly until I come across a problem, in which I try to assign two collision game objects to each character prefab in the container that can detect the player's collision box when it collides with a powerup.
It only detects the first playerContainer's (Milo) 'Coin Detect' game object (eventhough it's been deactivated) and does not recognize the 'Coin Detect' collision game object in the Baddie playerContainer (which the player has chosen to play)
So my question is, how am I able to get the script to recognize the child that's active in the third playerContainer game object instead of it automatically detecting the first playerContainer game object's child?
From the Magnet powerup script I've made, the script detects the first playerContainer's 'Coin Detect' game object only. As shown in the attached picture below:
Here's my current script where the player is able to select their preferred characters.
using System.Collections.Generic;
using UnityEngine;
public class EnablePlayerSelector : MonoBehaviour
{
public GameObject[] players;
public int currPlayerCount;
void Start()
{
currPlayerCount = PlayerPrefs.GetInt("SelectedPlayer", 0);
foreach (GameObject player in players)
player.SetActive(false);
players[currPlayerCount].SetActive(true);
}
}
and here's the script where I deactivate the 'Coin Detect' collision game Object and activate it when the player collides with the powerup.
using System.Collections.Generic;
using UnityEngine;
public class Magnet : MonoBehaviour
{
public GameObject coinDetect;
void Start()
{
coinDetect = GameObject.FindGameObjectWithTag("CoinDetect");
coinDetect.SetActive(false);
Debug.Log("False");
}
private void OnTriggerEnter(Collider other)
{
if(other.gameObject.tag == "Player")
{
StartCoroutine(coinActivate());
Destroy(transform.GetChild(0).gameObject);
}
}
IEnumerator coinActivate()
{
Debug.Log("Hi");
coinDetect.SetActive(true);
yield return new WaitForSeconds(5f);
coinDetect.SetActive(false);
}
}
Well your issue is rather that you are doing GameObject.FindGameObjectWithTag("CoinDetect") which will return whatever is the first encountered CoinDetect in your scene in this moment. So unless EnablePlayerSelector.Start is executed before the Magnet.Start it will always be the first one since they are still all active by then.
You could probably already solve your issue by following the general thumb-rule:
Do things where you do not depend on other components already in Awake
Do things where you o depend on other components in Start
This way you already cover most of the use cases and are sure that components are self-initialized (Awake) before anything is accessing them in Start.
So simply change the EnablePlayerSelector.Start into Awake and you should be fine.
In very specific cases you might still have to adjust the Script Execution Order or use events.
Instead of doing this at all though I would rather in the moment of the collision request the current one from the PlayerContainer directly:
// put this on each player root object
public class Player : MonoBehaviour
{
// reference these via the Inspector
[SerializeField] private GameObject coinDetector;
[SerializeField] private GameObject playerrange;
// ... and in general any information bout this Player that is relevant for other scripts
// Readonly accessor property
public GameObject CoinDetector => coinDetector;
}
and then rather have
public class EnablePlayerSelector : MonoBehaviour
{
[SerializeField] private Player[] players;
[SerializeField] private int currPlayerCount;
public Player CurrentPlayer => players[currPlayerCount];
// In general do things where you don't depend on other scripts already in Awake
// This way you could actually already solve your issue by keeping things where you do
// depend on others in Start
private void Awake()
{
currPlayerCount = PlayerPrefs.GetInt("SelectedPlayer", 0);
for(var i = 0; i < players.Length; i++)
{
players[i].gameObject.SetActive(i == currPlayerCount);
}
}
}
and then finally do e.g.
public class Magnet : MonoBehaviour
{
public float effectDuration = 5f;
private IEnumerator OnTriggerEnter(Collider other)
{
var playerSelector = other.GetComponentInParent<EnablePlayerSelector>();
if(!playerSelector) yield break;
Debug.Log("Hi");
Destroy(transform.GetChild(0).gameObject);
var coinDetect = playerSelector.CurrentPlayer.CoinDetector;
coinDetect.SetActive(true);
yield return new WaitForSeconds(effectDuration);
coinDetect.SetActive(false);
}
}
or alternatively if the other in this case refers to the object with the Player component anyway you could also directly do
public class Magnet : MonoBehaviour
{
public float effectDuration = 5f;
private IEnumerator OnTriggerEnter(Collider other)
{
if(!other.TryGetComponent<Player>(out var player)) yield break;
Debug.Log("Hi");
Destroy(transform.GetChild(0).gameObject);
var coinDetect = player.CoinDetector;
coinDetect.SetActive(true);
yield return new WaitForSeconds(effectDuration);
coinDetect.SetActive(false);
}
}
So I have a CharacterController Class that just deals with User inputs and If the player is hit by a bullet and reduces the Health of that Character. I need to use this Health Value in another Script so Have used the following code to try this however I get a CS01061 as it says it cannot find the Health variable.
Character Controller Class Variables:
public class CharacterController : MonoBehaviourPunCallbacks
{
public float MovementSpeed = 2;
public float JumpForce = 5;
public float Health = 100f;
public float height = 10;
public static GameObject LocalPlayerInstance;
Here is the code for the PlayerManager Class that needs Health from above:
//Gloabl Variables
public GameObject ActualPlayer;
public CharacterController Controller;
public float Health;
public void Awake()
{
ActualPlayer = GameObject.Find("Player");
Controller = ActualPlayer.GetComponent<CharacterController>();
Health = Controller.Health;
......
Player is a prefab that is a GameObject that has been initialised by Photon and has the CharacterController script as a component. Why can't unity see that the script has a health variable?
Are you sure it is looking for the correct type and not maybe one with the same name but from a different namespace?
Unity itself already has a built-in type named CharacterController in the namespace UnityEngine so most probably your second script is using that type since on the top you will have a
using UnityEngine;
Make sure you are referencing the correct one in your script.
You could e.g. put yours into a specific namespace like
namespace MyStuff
{
public class CharacterController : MonoBehaviourPunCallbacks
{
...
}
}
and then in your other script explicitly use
public MyStuff.CharacterController Controller;
and
Controller = ActualPlayer.GetComponent<MyStuff.CharacterController>();
Health = Controller.Health;
Besides that remember that float is a value type, not a reference, so most probably you storing that value in a field in your second script is not what you want to do ;)
rather always simply access the Controller.Health instead.
I'm new to Unity, and although I've looked this up a million times, I can't get my script to stop erroring. I am trying to reference a public float (attached to a different object) in a new script - which is simple, I know. I have tried various methods, but this is what I have so far. I keep getting the error that the float (currentHealth) cannot be implicitly converted to 'Health', which I understand but cannot seem to fix. What am I doing wrong?
public class sugarGenerator : MonoBehaviour
{
public GameObject sugar;
public GameObject Insulin;
public Transform generationPoint;
public float distanceBetween;
Health PlayerHealth;
// Update is called once per frame
public void Awake ()
{
GameObject.FindWithTag("Main_Girl_0");
PlayerHealth = GetComponent<Health>().currentHealth;
}
}
Based on my comment:
PlayerHealth is not a float, it is Health. You cannot assign a float (the type of currentHealth) to a Health.
I'm not familiar with Unity, but perhaps you meant to do:
PlayerHealth = GetComponent<Health>();
I have a few scripts which are divided between the Player and the game objects Underneath player. The scripts are the accuracy, player speed, and Projectile Damage.
I have created a menu where the player (When interacted with an NPC) could buy certain upgrades to increase these values throughout the whole game.
Now I am wondering, is it better to transfer all of these scripts to 1 script and access that. Or is it better to leave it divided between the scripts and call them all independently?
Since this is my first time upgrading player stats any help is welcome!
Lets start off with the Player Script where the max Run speed is.
public class Player : Actor {
public int playerNumber = 1;
// Run Speed & Acceleration
public float MaxRun = 90f; // Maximun Horizontal Run Speed
public float RunAccel = 1000f; // Horizontal Acceleration Speed
public float RunReduce = 400f; // Horizontal Acceleration when you're already when your horizontal speed is higher or equal to the maximum
The second script I have is in the weapon itself where I have the accuracy of the weapon.
public class Weapon : MonoBehaviour {
protected float currentAngle = 0f;
public float randomAngle = 20;
The last Script is where I have put the damage. Since the damage is within the projectile's I put everything in the projectile script.
//[RequireComponent (typeof(Rigidbody2D))]
public class Projectile : MonoBehaviour {
[Header ("Speed")]
public float baseSpeed;
public float randomSpeed;
public Vector2 SpeedV2;
public Vector2 Direction;
[Header ("Damage")]
public int DamageOnHit;
To put everything within my Upgrademenu I made a script calling the different scripts.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UpgradeMenu : MonoBehaviour
{
[SerializeField]
private Text accuracyText;
[SerializeField]
private Text speedText;
[SerializeField]
private Text damageText;
[SerializeField]
private float accuracyMultiplier = 0.7f;
private Weapon weapon;
private Projectile projectile;
private Player player;
void OnEnable()
{
UpdateValues();
}
void UpdateValues ()
{
accuracyText.text = weapon.randomAngle.ToString();
damageText.text = projectile.DamageOnHit.ToString();
speedText.text = player.MaxRun.ToString();
}
public void UpgradeAccuracy ()
{
weapon.randomAngle = (int)weapon.randomAngle * accuracyMultiplier;
UpdateValues();
}
public void UpgradeDamage ()
{
projectile.DamageOnHit = (int)projectile.DamageOnHit + 1;
UpdateValues();
}
}
I'm pretty sure that the way I'm trying to call my other scripts within the UpgradeMenu script is wrong. So if anyone is able to help that would be great!
As you can see the machinegun is a child of the player. Because you are able to pick up this item. This already causes an issue with the gun. Since I can change the value from within Unity, but when I pick up another machine gun, this value goes back to the usual 4 value.
Aside from that, I have a button which has an onclick value. The idea is that when a player clicks on this button (Still need to change the UI) the value should be changed with the use of this UpgradeMenu script.
For now the buttons work, the only problem is that the upgrade functionalities "are not set to an instance of an object"
You should distinguish between configuration data, and logic data.
Configuration data does not change. e.g. Weapon magazine size.
Logic data does change. e.g. Bullets in the magazine.
Logic data is unique and only exists within some object instance, while configuration data exists in a database and is identifying by an id.
The following is just an example code that demonstrates the idea.
Data:
public class WeaponData
{
public string id;
public float spread;
public string projectileId;
}
public class ProjectileData
{
public string id;
public int damage;
public string prefabPath;
}
Library:
public static class Library
{
public static Dictionary<string, WeaponData> Weapons;
public static Dictionary<string, ProjectileData> Projectiles;
// add more ...
}
Configuration Setup:
WeaponData weapon = new WeaponData
{
id = "shotgun",
spread = 20f,
projectileId = "shotgun_shell"
};
ProjectileData projectile = new ProjectileData
{
id = "shotgun_shell",
damage = 100,
prefabPath = "Projectiles/ShotgunShell"
};
Library.Weapons.Add(weapon.id, weapon);
Library.Weapons.Add(projectile.id, projectile);
Logic:
public class Weapon
{
public WeaponData weapon = Library.Weapons["shotgun"];
public void Shoot()
{
GameObject.Instantiate(
Resources.Load<Projectile>(Library.Projectiles[weapon.projectileId].prefabPath)
);
}
}
For an upgrade system to work, you could setup multiple weapons:
"shotgun_v1" -> "shotgun_v2" -> "shotgun_v3"
Note: Another approach would be to use ScriptableObjects, that is if you like serializing data in the Unity editor. Cool video on the topic.
I believe your issue lies where you're trying to set randomAngle on a class type, which is not a static property:
public void UpgradeAccuracy ()
{
Weapon.randomAngle = (int)Weapon.randomAngle * accuracyMultiplier;
UpdateValues();
}
What is Weapon here? Just a class name, you can't set or get these things since it's not instantiated. What I believe you want to do is:
public void UpgradeAccuracy ()
{
angle.randomAngle = (int)angle.randomAngle * accuracyMultiplier;
UpdateValues();
}
Other than that, there are probably not much wrong with what you're doing. It's probably not the architecture I'd choose but I haven't seen your entire game and there might be a reason for your choices; a tip for the future would be that not everything has to be Components (derive from MonoBehaviour), you can work with "normal" classes as well when creating games in Unity if you don't need the component behaviour. I generally try to keep as few MonoBehaviour components scripts as possible.
I'm trying to learn how to use Unity and following online tutorials but I am currently having a problem that I don't understand how to fix.
I have a Sprite in my scene and I have attached a script to it however in the Inspector it shows the script is there but I cannot see the variables inside? I had this problem previously and it sorted itself out.
What is the cause of this problem/how do I fix it?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpaceShip : MonoBehaviour {
public float speed = 30;
public GameObject theBullet;
private void FixedUpdate()
{
float horzMove = Input.GetAxisRaw("Horizontal");
GetComponent<Rigidbody2D>().velocity = new Vector2(horzMove, 0) *
speed;
}
// Update is called once per frame
void Update () {
if (Input.GetButtonDown("Jump"))
{
Instantiate(theBullet, transform.position, Quaternion.identity);
}
}
}
Edit: The problem was solved by reimporting.
You either need to declare the variables as Public or [SerializeField] for member variables to appear in the inspector. Note that by declaring something as public allows access to the variable from outside the class (from other scripts/classes for example). By default, private is assigned to member variables.
Example:
public class testscript : MonoBehaviour
{
public int foo; // shows up in inspector
[SerializeField] private int bar; // also shows up while still being private
void Start()
{
}
}
Not is a problem, You forget to do something surely.
It is common at first with Unity.
Start again.
In the scene create a new GameObject and add you script.
If the inspector shows not variable:
The varible do not is public (false, if is public in you script)
There is some syntax error in the script!
or
You were not adding the correct script to the GameObject.
There are not many secrets to that, if all is well enough that the variable is public and this outside of a method of the script so that it is seen in the inspector.
One tip, do not use a GetComponent or Instantiate inside a FixedUpdate or Update because they are expensive, save the Rigidbody2D in a variable in the Start and then use it.
Sorry for my English and good luck.