unity public variable not showing with gameObject.find - c#

I have a script here called EnemyHealth and a script called DatabaseManager. in the DatabaseManager script I am trying to reference a newly created public int deathCount variable from the EnenmyHealth script, but that public variable isn't showing for me while all others are, here is the code
EnemyHealth
using UnityEngine;
namespace CompleteProject
{
public class EnemyHealth : MonoBehaviour
{
public int startingHealth = 100; // The amount of health the enemy starts the game with.
public bool isDead; // Whether the enemy is dead.
public int deathCount;
public int currentHealth; // The current health the enemy has.
public float sinkSpeed = 2.5f; // The speed at which the enemy sinks through the floor when dead.
public int scoreValue = 10; // The amount added to the player's score when the enemy dies.
public AudioClip deathClip; // The sound to play when the enemy dies.
DatabaseManager
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public class DatabaseManager: MonoBehaviour
{
private void Awake()
{
int x = GameObject.Find("ZomBear").GetComponent<EnemyHealth>().deathCount;
}
it just doesn't seem to find this one variable whilst all the other public ones are visible

The class EnemyHealth is inside the CompleteProject namespace: you should add
using CompleteProject
at the top of DatabaseManager script, in order to have visibility of that namespace.

In your IDE hit save all. ensure that when you edit multiple scripts, you save the nw version of all scripts involved. sounds simple, but i have done this hundreds of times, and it usually only takes a sec to realize it.

Related

Random Obstacle Generator in unity 3D

I am looking to make my first actual game called Bottomless. It seems I am having a problem with the obstacle generator script in Unity2D. I want the obstacle to generate vertically, where a player is falling down the level.
Here my script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Generator : MonoBehaviour
{
public GameObject spike;
public float maxspeed;
public float minspeed;
public float currentspeed;
void Awake()
{
currentspeed = Random.Range(maxspeed, minspeed);
spikegen();
}
void spikegen()
{
GameObject SpikeIns = Instantiate(spike, transform.position, transform.rotation);
}
void Update()
{
}
}
Seems like running this code crashes unity quickly. Does anyone have better alternative?
So, I have been considering your problem, and I think the best thing to do would be to create spawn points in your map pieces for the spikes to fit in. Then, when your player is falling, and the new map is loaded, grab the spawn points, add them to a list, choose one randomly, and then instantiate your spike there. Or get the map pieces themselves to spawn the spikes as they are loaded.
Method 1:
Create spawn points on your map piece. Create and add the following script. Place the spawn points in the list in inspector. And call function on Awake/Start.
using System.Collections;
using UnityEngine;
public class MapScript : MonoBehaviour
{
//Spike you want to instantiate
public GameObject spike;
//List for you to place spawn points in the inspector
public List<GameObject> Spawnpoints = new List<GameObject>();
public void Awake()
{
//Get a random number from 0 to our lists count
int random = Random.Range(0, Spawnpoints.Count);
//Instantiate at the random spawn point
GameObject SpikeIns = Instantiate(spike, Spawnpoints[random].transform.position, transform.rotation);
}
}
Method 2:
Similar to the example above, except the code to spawn happens in the Generator script (this is not the preferred method as we will have to access the map piece's script).
So use the code above but remove the Awake and spike GameObject, and then for your generator script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Generator : MonoBehaviour
{
public GameObject spike;
public float maxspeed;
public float minspeed;
public float currentspeed;
void Awake()
{
currentspeed = Random.Range(maxspeed, minspeed);
spikegen();
}
void Update()
{
}
void spikegen()
{
List<GameObject> Spawners = new List<GameObject>();
foreach(GameObject spawn in refernceToOtherScripts.Spawnpoints)
{
Spawners.Add(spawn);
}
int random = Random.Range(0, Spawners.Count);
GameObject SpikeIns = Instantiate(spike, Spawners[random].transform.position, transform.rotation);
}
}
Neither of these have been tested as I am on my work computer, but they should both work perfectly fine. As you have probably guessed, there are a lot of different way to go about this.

Scoring points using triggers in Unity

I want that whenever my player passes through a particular portion of my obstacle it should add 2 points to the score. In order to do this I've made a child of the obstacle. This child contains the box collider which covers that particular portion of the obstacle (I've switched on the Is Trigger in Unity).
Code on child having trigger -
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Score : MonoBehaviour
{
float points;
void Start()
{
}
void Update()
{
Debug.Log(points);
}
void OnTriggerExit2D(Collider2D other)
{
points += 2f;
}
}
The problem is that in the console the points are showing 0s and 2s only like this -
Console
While it should be 0, 2, 4, 6... after passing the obstacle.
Also clones of the original obstacle are being created, i.e. I pass through a new clone each time; in case this is causing the problem.
Yeah it is obvious that it will print out 0s and 2s because your script doesn't use a centralized scoring system it just updates the score in that particular instance. You can make another script call it GameManager where you can make a player score variable and on trigger exit function of your child function would increment the score of that GameManager variable playerscore.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public float playerPoints = 0f;
}
In Score Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Score : MonoBehaviour
{
public GameManager manager; // Drag and drop the gamemanager object from scene in to this field
void OnTriggerExit2D(Collider2D other)
{
manager.playerPoints += 2f;
Debug.Log(manager.playerPoints);
}
}

Why does C# Not see the variable in another Script when Using GetComponent? C#

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.

Sync UNet death across all clients in unity3D

I've been learning UNet to create a LAN multiplayer game and everything is going really well except the player damage.
The goal here is to sync the death enough that both clients realize the death has happened, and it is supposed to reload the scene, using NetworkManager.singleton.ServerChangeScene("SampleScene");
However the death only updates on one client, but a new error has appeared. When I acquire and call one of the methods from my PlayerDamage script, it returns an error saying there is no argument in the method that corresponds to my float amount constructor. Here is the code from my Gun and PlayerDamage script.
Gun:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
public class Gun : NetworkBehaviour {
public float damage = 10f;
public float range = 100f;
public Camera fpsCam;
public AudioSource pistol;
//public ParticleSystem muzzleFlash;
//[SerializeField] private ParticleSystem part;
//public float shotTime = .25f;
private void Update()
{
if (Input.GetButton("Fire1"))
{
Shoot();
pistol.Play();
}
}
void Shoot()
{
RaycastHit hit;
if (Physics.Raycast(fpsCam.transform.position, fpsCam.transform.forward, out hit, range))
{
PlayerDamage target = hit.transform.GetComponent<PlayerDamage>();
target.takeDamage();
}
}
}
PlayerDamage:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
public class PlayerDamage : NetworkBehaviour {
[SyncVar]
public float health = 50f;
[ClientRpc]
public void CmdTakeDamage(float amount)
{
Debug.Log("took damage" + amount);
}
public void takeDamage(float amount)
{
health -= amount;
CmdTakeDamage(amount);
}
public void Die()
{
NetworkManager.singleton.ServerChangeScene("SampleScene");
}
}
Thanks in advance, FerretCode
You'll need to send a message across all other clients in order for them to be updated on a certain player. In order to do that, you'll need to have some sort of a game event bus that handles the receiving and dispatching of game events throughout the network.
Say if you have three players playing your game and one of them dies, it sends a network message to the host (regardless if it's a server or a player) that a player X died. The host will now send a message to other players Y, and Z, except X that player X died and thus notifying them so that clients Y and Z will be able to kill player X on their side.
Multiplayer games are harder than it looks, and you'll need to design a system where can make every one of the clients synchronize all at the same time before doing any gameplay-related stuff.

Upgrading the Character

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.

Categories