This is the code for detecting images with ARFoundation and enabling objects depending on the image the device is scanning
[SerializeField]
public GameObject[] placeablePrefabs;
private Dictionary<string, GameObject> spawnedPrefabs = new Dictionary<string, GameObject>();
private ARTrackedImageManager trackedImageManager;
private JSONObject warnings;
public void Awake()
{
trackedImageManager = GetComponent<ARTrackedImageManager>();
foreach(GameObject prefab in placeablePrefabs)
{
GameObject newPrefab = Instantiate(prefab, Vector3.zero, Quaternion.identity);
newPrefab.name = prefab.name;
spawnedPrefabs.Add(prefab.name, newPrefab);
}
}
private void OnEnable()
{
trackedImageManager.trackedImagesChanged += ImageChanged;
}
private void OnDisable()
{
trackedImageManager.trackedImagesChanged -= ImageChanged;
}
private void ImageChanged(ARTrackedImagesChangedEventArgs eventArgs)
{
foreach(ARTrackedImage trackedImage in eventArgs.added)
{
UpdateImage(trackedImage);
}
foreach (ARTrackedImage trackedImage in eventArgs.updated)
{
UpdateImage(trackedImage);
}
foreach (ARTrackedImage trackedImage in eventArgs.removed)
{
spawnedPrefabs[trackedImage.name].SetActive(false);
}
}
private void UpdateImage(ARTrackedImage trackedImage)
{
string name = trackedImage.referenceImage.name;
Vector3 position = trackedImage.transform.position;
GameObject prefab = spawnedPrefabs[name];
prefab.transform.position = position;
prefab.SetActive(true);
foreach(GameObject go in spawnedPrefabs.Values)
{
if(go.name != name)
{
go.SetActive(false);
}
}
}
It is working as expected when I am switching from one image to another, but, if I switch back, nothing happens, the objects wont switch visible state again. They do only on the first Switch.
Problem was that eventArgs.removed did not get called. Do not know why.
I use 3.1.3 ARFoundation and ARCore versions.
Anyway, I managed to find a solution. I will post the whole code in case someone wants the whole think.
It goes like this:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using System;
using UnityEngine.UI;
using UnityEngine.XR.ARSubsystems;
public class ImageRecognitionExample : MonoBehaviour
{
private ARTrackedImageManager m_ImageManager;
[SerializeField]
private GameObject welcomePanel;
[SerializeField]
private Button dismissButton;
[SerializeField]
private Text imageTrackedText;
[SerializeField]
private GameObject[] arObjectsToPlace;
[SerializeField]
private Vector3 scaleFactor = new Vector3(0.1f, 0.1f, 0.1f);
private ARTrackedImageManager m_TrackedImageManager;
private Dictionary<string, GameObject> arObjects = new Dictionary<string, GameObject>();
String currentActiveQR;
void Awake()
{
dismissButton.onClick.AddListener(Dismiss);
m_ImageManager = GetComponent<ARTrackedImageManager>();
foreach (GameObject arObject in arObjectsToPlace)
{
GameObject newARObject = Instantiate(arObject, Vector3.zero, Quaternion.identity);
newARObject.name = arObject.name;
arObjects.Add(arObject.name, newARObject);
}
}
void OnEnable()
{
m_ImageManager.trackedImagesChanged += OnTrackedImagesChanged;
}
void OnDisable()
{
m_ImageManager.trackedImagesChanged -= OnTrackedImagesChanged;
}
void OnTrackedImagesChanged(ARTrackedImagesChangedEventArgs eventArgs)
{
foreach (ARTrackedImage trackedImage in eventArgs.added)
{
currentActiveQR = trackedImage.referenceImage.name;
UpdateARImage(trackedImage, currentActiveQR);
}
foreach (ARTrackedImage trackedImage in eventArgs.updated)
{
if (trackedImage.trackingState == TrackingState.Tracking)
{
currentActiveQR = trackedImage.referenceImage.name;
imageTrackedText.text = currentActiveQR;
AssignGameObject(currentActiveQR, trackedImage.transform.position);
}
else
{
if (currentActiveQR != trackedImage.referenceImage.name)
{
ReAssignGameObject(trackedImage.referenceImage.name, trackedImage.transform.position);
}
}
}
}
private void UpdateARImage(ARTrackedImage trackedImage, String name)
{
imageTrackedText.text = trackedImage.referenceImage.name;
AssignGameObject(name, trackedImage.transform.position);
}
void AssignGameObject(string name, Vector3 newPosition)
{
if (arObjectsToPlace != null)
{
GameObject goARObject = arObjects[name];
goARObject.SetActive(true);
goARObject.transform.position = newPosition;
goARObject.transform.localScale = scaleFactor;
foreach (GameObject go in arObjects.Values)
{
//Debug.Log($"Go in arObjects.Values: {go.name}");
if (go.name != name)
{
go.SetActive(false);
}
}
}
}
void ReAssignGameObject(string name, Vector3 newPosition)
{
if (arObjectsToPlace != null)
{
GameObject goARObject = arObjects[name];
goARObject.SetActive(true);
goARObject.transform.position = newPosition;
goARObject.transform.localScale = scaleFactor;
foreach (GameObject go in arObjects.Values)
{
if (go.name == name)
{
go.SetActive(false);
}
}
}
}
private void Dismiss() => welcomePanel.SetActive(false);
}
Related
I have a small game where the player flies a rocket to a green platform to progress. If they crash, they start over. The levels simply loop and the gameplay works fine. The issue I'm having is with the timer. I'm trying to make a system that keeps track of how long it takes the player to beat every level, and then save their fastest time under the record variable. I tried adding a hotkey in the Timer script to reset the timer, but if I run the same ResetTimer function from the Rocket script, the currentTime variable will not change. It still prints "Timer reset" but the variable stays the same. Any ideas?
Rocket.cs
using UnityEngine.SceneManagement;
public class Rocket : MonoBehaviour
{
Timer timer = new Timer();
[SerializeField] float rotationThrust = 100f;
[SerializeField] float mainThrust = 50f;
[SerializeField] float levelLoadDelay = 2f;
[SerializeField] AudioClip mainEngine;
[SerializeField] AudioClip success;
[SerializeField] AudioClip death;
[SerializeField] ParticleSystem mainEngineParticles;
[SerializeField] ParticleSystem successParticles;
[SerializeField] ParticleSystem deathParticles;
Rigidbody rigidBody;
AudioSource audioSource;
enum State { Alive, Dying, Transcending }
State state = State.Alive;
bool collisionsEnabled = true;
void Start()
{
rigidBody = GetComponent<Rigidbody>();
audioSource = GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.Escape))
{
Application.Quit();
}
if (state == State.Alive)
{
RespondToThrustInput();
RespondToRotateInput();
}
if (Debug.isDebugBuild)
{
RespondToDebugKeys();
}
}
private void RespondToDebugKeys()
{
if (Input.GetKeyDown(KeyCode.L))
{
LoadNextLevel();
}
else if (Input.GetKeyDown(KeyCode.K))
{
LoadFirstLevel();
}
else if (Input.GetKeyDown(KeyCode.C))
{
collisionsEnabled = !collisionsEnabled;
}
else if (Input.GetKeyDown(KeyCode.P))
{
timer.ResetRecord();
}
}
private void RespondToThrustInput()
{
if (Input.GetKey(KeyCode.Space) || Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow))
{
ApplyThrust();
}
else
{
audioSource.Stop();
mainEngineParticles.Stop();
}
}
private void ApplyThrust()
{
rigidBody.AddRelativeForce(Vector3.up * mainThrust);
if (!audioSource.isPlaying)
{
audioSource.PlayOneShot(mainEngine);
}
mainEngineParticles.Play();
}
private void RespondToRotateInput()
{
rigidBody.freezeRotation = true;
float rotationSpeed = rotationThrust * Time.deltaTime;
if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
{
transform.Rotate(Vector3.forward * rotationSpeed);
}
else if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))
{
transform.Rotate(Vector3.back * rotationSpeed);
}
rigidBody.freezeRotation = false;
}
void OnCollisionEnter(Collision collision)
{
if (state != State.Alive || !collisionsEnabled)
{
return;
}
switch (collision.gameObject.tag)
{
case "Friendly":
break;
case "Finish":
StartSuccessSequence();
break;
default:
StartDeathSequence();
break;
}
}
private void StartSuccessSequence()
{
state = State.Transcending;
audioSource.PlayOneShot(success);
successParticles.Play();
Invoke("LoadNextLevel", levelLoadDelay);
}
private void StartDeathSequence()
{
state = State.Dying;
audioSource.Stop();
deathParticles.Play();
audioSource.PlayOneShot(death);
Invoke("LoadFirstLevel", levelLoadDelay);
}
public void LoadFirstLevel()
{
timer.ResetTimer();
SceneManager.LoadScene(1);
}
private void LoadNextLevel()
{
int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
if (currentSceneIndex + 1 == SceneManager.sceneCountInBuildSettings)
{
timer.Save();
LoadFirstLevel();
}
else
{
SceneManager.LoadScene(currentSceneIndex + 1);
}
}
}
Timer.cs
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;
using UnityEngine.UI;
using System;
public class Timer : MonoBehaviour
{
float currentTime;
float record = -1f;
public Text currentText;
public Text recordText;
private void Start()
{
DontDestroyOnLoad(gameObject);
}
public void Save()
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Create(Application.persistentDataPath + "/playerInfo.dat");
PlayerData data = new PlayerData();
if (RecordTest() == true)
{
if (currentTime < record)
{
record = currentTime;
data.recordSave = record;
}
}
else record = currentTime;
data.recordSave = record;
bf.Serialize(file, data);
file.Close();
}
public void Load()
{
if(File.Exists(Application.persistentDataPath + "/playerInfo.dat"))
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/playerInfo.dat", FileMode.Open);
PlayerData data = (PlayerData)bf.Deserialize(file);
file.Close();
record = data.recordSave;
}
}
private void Update()
{
CheckForResetInput();
SetCurrentTimeText();
SetRecordTimeText();
}
private void SetRecordTimeText()
{
if (record == -1)
{
recordText.text = "N/A";
}
else
{
string minutesRecord = Mathf.Floor(record / 60).ToString("00");
string secondsRecord = Mathf.Floor(record % 60).ToString("00");
recordText.text = minutesRecord + ":" + secondsRecord;
}
}
private void CheckForResetInput()
{
if (Input.GetKeyDown(KeyCode.R))
{
ResetTimer();
}
}
private void SetCurrentTimeText()
{
currentTime += Time.deltaTime;
string minutes = Mathf.Floor(currentTime / 60).ToString("00");
string seconds = (currentTime % 60).ToString("00");
currentText.text = minutes + ":" + seconds;
}
public void ResetTimer()
{
print("Timer reset");
currentTime = 0;
}
public void ResetRecord()
{
record = -1f;
}
public bool RecordTest()
{
if (record == -1)
{
return false;
}
else return true;
}
public void Collided()
{
print("Ouch");
}
}
[Serializable]
class PlayerData
{
public float recordSave;
}
Serializedfields with no other modifier are PRIVATE to the class they are in. Serializedfields are purely for them to show in the unity inspector, they need to be public for you to access them from other classes
So
[SerializeField] float rotationThrust = 100f; is private to Rocket
[SerializeField] public float rotationThrust = 100f; will be visible to other classes with a reference to a Rocket instance - Technically you dont need to serialize the field as public will always show, but it makes it look nice in your code if they all line up :D
You are creating a new instance of Timer for your Rocket component.
Timer timer = new Timer();
This means that when rocket calls timer.ResetTimer() is resetting the newly instanced Timer class, but NOT the MonoBehaviour attached to your scene object.
Rather than declare a new timer in your rocket class, create a reference to it in the inspector via:
[SerializeField] Timer timer;
and drag/drop your Timer object to that reference.
The above solution is probably the best pattern, but if you want a quick and easy solution, change your currentTime to static:
private static float currentTime
Making this value static means that all Timer instances will share the same value. Since the variable is static, it is not specific to a single class instance. With this solution you are not eliminating the multiple instances of Timer, but you are forcing them to share a currentTime variable. This doesn't fix the design that caused your problem, but it does fix the symptom. This is why I say it is not the best solution.
I'm creating a FPS game and I have the following issue:
Sometimes, when I shoot at the enemies the hit is not recognized, even if the player is shooting in front of them. However, when they attack the player, the hit is recognized normally.
They have a box collider and a rigidbody attached to them.
This script is attached to the player:
using System.Collections.Generic;
using UnityEngine;
public class DisparaArma : MonoBehaviour
{
private GerenciaArma gerenciaArma;
public float nDisparo = 15f;
private float TempoProximoDisparo;
public float damage = 20f;
private Animator ZoomCameraIn;
private bool zoomed;
private Camera Maincamera;
private GameObject mira;
// Start is called before the first frame update
void Start()
{
gerenciaArma = GetComponent<GerenciaArma>();
ZoomCameraIn = transform.Find(Tags.LOOK_ROOT).transform.Find(Tags.ZOOM_CAMERA).GetComponent<Animator>();
mira = GameObject.FindWithTag(Tags.MIRA);
Maincamera = Camera.main;
}
// Update is called once per frame
void Update()
{
Atira();
ZoomInAndOut();
}
void Atira()
{
if (Input.GetMouseButtonDown(0))
{
if(gerenciaArma.SelecionaArma().tipoBala == WeaponBulletType.BULLET)
{
gerenciaArma.SelecionaArma().AnimacaoTiro();
DisparaBala();
}
}
}
void ZoomInAndOut()
{
if (gerenciaArma.SelecionaArma().mira_tipo == TipoMira.AIM)
{
if (Input.GetMouseButtonDown(1))
{
ZoomCameraIn.Play(Animacoes.ZOOM_IN_ANIM);
// gerenciaArma.SelecionaArma().Aim(true);
mira.SetActive(false);
print("VaiZoom");
}
if (Input.GetMouseButtonUp(1))//
{
ZoomCameraIn.Play(Animacoes.ZOOM_OUT_ANIM);
//gerenciaArma.SelecionaArma().Aim(false);
mira.SetActive(true);
}
}
}
void DisparaBala()
{
RaycastHit hit;
if(Physics.Raycast(Maincamera.transform.position, Maincamera.transform.forward, out hit))
{
if (hit.transform.tag == Tags.ENEMY_TAG)
{
hit.transform.GetComponent<ScriptVida>().DanoAplicado(damage);
}
}
}
}
And this one is attached to the enemies:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class ScriptVida : MonoBehaviour
{
private IndioAnimações indio_Anim;
private NavMeshAgent navAgent;
private IndioController indio_Controller;
public float vida = 100f;
public bool is_Player, is_Cannibal, is_Tiger;
private bool morto;
// Start is called before the first frame update
void Awake()
{
if (is_Tiger || is_Cannibal)
{
indio_Anim = GetComponent<IndioAnimações>();
indio_Controller = GetComponent<IndioController>();
navAgent = GetComponent<NavMeshAgent>();
}
if (is_Player)
{
}
}
public void DanoAplicado(float damage)
{
if (morto)
return;
vida -= damage;
if (is_Player)
{
}
if (is_Tiger || is_Cannibal)
{
if (indio_Controller.EnemyState == EnemyState.PATROL)
{
indio_Controller.chase_Distance = 50f;
}
}
if (vida <= 0)
{
JogadorMorre();
morto = true;
print(vida);
}
}
void JogadorMorre()
{
if (is_Cannibal)//
{
GetComponent<Animator>().enabled = false;
GetComponent<BoxCollider>().isTrigger = false;
GetComponent<Rigidbody>().AddTorque(-transform.forward * 50f);
indio_Controller.enabled = false;
navAgent.enabled = false;
indio_Anim.enabled = false;
}
if (is_Tiger)
{
GetComponent<Animator>().enabled = false;
GetComponent<BoxCollider>().isTrigger = false;
GetComponent<Rigidbody>().AddTorque(-transform.forward * 50f);
indio_Controller.enabled = false;
navAgent.enabled = false;
indio_Anim.enabled = false;
}
if (is_Player)
{
GameObject[] enemies = GameObject.FindGameObjectsWithTag(Tags.ENEMY_TAG);
for (int i = 0; i < enemies.Length; i++)
{
enemies[i].GetComponent<IndioController>().enabled = false;
}
GetComponent<Movimentação>().enabled = false;
GetComponent<DisparaArma>().enabled = false;
GetComponent<GerenciaArma>().SelecionaArma().gameObject.SetActive(false);
}
if (tag == Tags.PLAYER_TAG)
{
Invoke("RestartGame", 3f);
}
else
{
Invoke("TurnOffGameObject", 3f);
}
}
void RestartGame()
{
UnityEngine.SceneManagement.SceneManager.LoadScene("Gameplay");
}
void TurnOffGameObject()
{
gameObject.SetActive(false);
}
}
I think the problem is related to the box collider.
How could I solve this guys?
I've just updated to the new Input System from 2.7 to 2.8.
How the new Input System works is you create an input actions asset by going to Create-> Input Actions
.
This creates an asset where actions can be mapped to keys. One then create a C# script from this asset and use it in their code. Which is what I did. I called the Asset MyInput.inputactions and the C# script is MyInput.cs
When you use the generated C# script this way you need to reference the asset in your script. However, after the update, it seems this is impossible to do from the editor. When I define a public MyInput variable in my class, like so:
public class ShapeMover: MonoBehaviour
{
public MyInput controls;
private float _lastFallTime;
private float _fallSpeed;
private ShapeSpawner _spawn;
private GameObject _shapeToMove;
private Transform _shapeToMoveTransform;
private bool _isGameOver;
private const float _leftRotationAngle = (float) -1.57079633;
private const float _rightRotationAngle = (float) 1.57079633;
}
It isn't exposed in the inspector:
And I get an obvious NullReferenceExceptionerror when I try to access the controls variable.
Am I doing something wrong?
How can I reference the asset from the inspector? I have tried adding [SerializeField] to the public declaration, it didn't help.
I was following this video and it worked fine until I updated to a newer Input System version.
For reference, this is the full ShapeMover class:
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
namespace _Scripts
{
public class ShapeMover : MonoBehaviour
{
[SerializeField]
public MyInput controls;
private float _lastFallTime;
private float _fallSpeed;
private ShapeSpawner _spawn;
private GameObject _shapeToMove;
private Transform _shapeToMoveTransform;
private bool _isGameOver;
private const float _leftRotationAngle = (float) -1.57079633;
private const float _rightRotationAngle = (float) 1.57079633;
private void Awake()
{
_spawn = FindObjectOfType<ShapeSpawner>();
_lastFallTime = 0f;
_fallSpeed = GameGrid.Instance.GetFallSpeed();
_isGameOver = false;
Debug.Log("Registering controls callbacks...");
controls.Player.Movement.performed += ctx => Movement(ctx.ReadValue<Vector2>(), true);
controls.Player.Drop.performed += ctx => Drop();
controls.Menu.Reset.performed += ctx => Restart();
controls.Menu.Pause.performed += ctx => PauseToggle();
SetShapeToMove();
}
private void Restart()
{
GameGrid.Instance.ResetGame();
_isGameOver = false;
SetShapeToMove();
}
private void PauseToggle()
{
Debug.Log("Got Pause input");
var currentPauseState = GameGrid.Instance.IsPaused;
//If not paused, will pause
if (!currentPauseState)
{
// controls.Player.Movement.Disable();
// controls.Player.Drop.Disable();
// controls.Player.Menu.Disable();
// controls.Player.Disable();
GameGrid.Instance.IsPaused = true;
}
else
{
// controls.Player.Movement.Enable();
// controls.Player.Drop.Enable();
// controls.Player.Menu.Enable();
// controls.Player.Enable();
GameGrid.Instance.IsPaused = false;
}
}
private void Drop()
{
// Debug.Log("Should Drop Shape!");
bool didMove = true;
while (didMove)
{
didMove = Movement(new Vector2(0, -1), false);
}
}
private bool Movement(Vector2 direction, bool isFromInput)
{
if (isFromInput)
{
Debug.Log($"Got input {direction.ToString()}");
}
//Disable movement controls when game is over.
if (_isGameOver)
{
return false;
}
var oldPosition = _shapeToMoveTransform.position;
var oldRotation = _shapeToMoveTransform.rotation;
// Transform[] children = _shapeToMoveTransform.Cast<Transform>().ToArray();
var didMove = true;
var didEndMovement = false;
GameGrid.Instance.RemoveShapeFromGrid(_shapeToMoveTransform);
if (direction.x < 0)
{
didMove = MoveLeft();
}
else if (direction.x > 0)
{
didMove = MoveRight();
}
else if (direction.y > 0)
{
didMove = RotateLeft();
}
else if (direction.y < 0)
{
didMove = MoveDown();
if (!didMove)
{
didEndMovement = true;
}
}
//If Shape didn't move, restore previous position.
if (!didMove)
{
_shapeToMoveTransform.position = oldPosition;
_shapeToMoveTransform.rotation = oldRotation;
}
GameGrid.Instance.AddShapeToGrid(_shapeToMoveTransform);
// Debug.Log($"Shape {_shapeToMove.name} Position after movement Did Move: {didMove.ToString()}");
// Transform[] children = _shapeToMoveTransform.Cast<Transform>().ToArray();
// var lowestChild = children.OrderBy(x => x.position.y).First();
// Debug.Log($"{lowestChild.position.ToString()}");
if (didEndMovement)
{
GameGrid.Instance.ClearRows(_shapeToMoveTransform);
_isGameOver = GameGrid.Instance.IsGameOver(_shapeToMoveTransform);
if (!_isGameOver)
{
SetShapeToMove();
}
}
return didMove;
}
private void SetShapeToMove()
{
_shapeToMove = _spawn.SpawnShape();
_shapeToMoveTransform = _shapeToMove.transform;
}
private void Update()
{
if (_isGameOver)
{
return;
}
if (GameGrid.Instance.IsPaused)
{
return;
}
var time = Time.time;
if (!(time - (_lastFallTime + _fallSpeed) > 0))
{
return;
}
Movement(new Vector2(0, -1), false);
_lastFallTime = time;
_fallSpeed = GameGrid.Instance.GetFallSpeed();
}
private bool MoveLeft()
{
_shapeToMoveTransform.position += Vector3.right;
return GameGrid.Instance.CanMove(_shapeToMoveTransform);
}
private bool MoveRight()
{
_shapeToMoveTransform.position += Vector3.left;
return GameGrid.Instance.CanMove(_shapeToMoveTransform);
}
private bool MoveDown()
{
_shapeToMoveTransform.position += Vector3.down;
return GameGrid.Instance.CanMove(_shapeToMoveTransform);
}
private bool RotateLeft()
{
_shapeToMoveTransform.Rotate(0, 0, -90);
// foreach (Transform child in _shapeToMoveTransform)
// {
// RotateTransform(child, _leftRotationAngle);
// }
return GameGrid.Instance.CanMove(_shapeToMoveTransform);
}
private void RotateTransform(Transform transformToRotate, float rotationAngleRadian)
{
var currentLocalPosition = transformToRotate.localPosition;
var currentX = currentLocalPosition.x;
var currentY = currentLocalPosition.y;
var rotatedX = currentX * Mathf.Cos(rotationAngleRadian) - currentY * Mathf.Sin(rotationAngleRadian);
var rotatedY = currentX * Mathf.Sin(rotationAngleRadian) + currentY * Mathf.Cos(rotationAngleRadian);
transformToRotate.localPosition = new Vector2(rotatedX, rotatedY);
// Debug.Log($"Position after rotation is: {transformToRotate.localPosition.ToString()}");
}
private bool RotateRight()
{
_shapeToMoveTransform.Rotate(0, 0, -90);
return GameGrid.Instance.CanMove(_shapeToMoveTransform);
}
private void OnEnable()
{
Debug.Log("Controls Enabled...");
controls.Enable();
}
// private void OnDisable()
// {
// Debug.Log("Controls Disabled...");
// controls.Disable();
// }
}
}
Just as you said, you can't reference the new generated input class anymore.
To make it works, i instantiated the class, and use the SetCallbacks method, like this :
private MyInput _inputs;
public void Awake()
{
_inputs = new MyInput();
}
Truth be told, i don't know if it's the intended way of using the input class, but it works.
EDIT :
Starting from the 2.8 preview, an interface is automatically generated. I can only recommend it, cause it's very easy to use, you just need to inherits from IYourActionsSetNameActions and add the callbacks. (Also, you have to enable / disable the actions set, but you should be able to do it in another script)
Here is a complete base example, using your naming :
public class ShapeMover : MonoBehaviour, MyInput.IPlayerActions
{
private MyInput _inputs;
public void Awake()
{
_inputs = new MyInput();
_inputs.Player.SetCallbacks(this);
}
public void OnEnable()
{
_inputs.Player.Enable();
}
public void OnDisable()
{
_inputs.Player.Disable();
}
public void OnMovement(InputAction.CallbackContext context)
{
Vector2 delta = context.ReadValue<Vector2>();
transform.position += new Vector3(delta.x, 0, delta.y);
}
public void OnDrop(InputAction.CallbackContext context)
{
//TODO
}
// ...
}
Enemy character not moving to the 3rd waypoint. After moving to waypoint 2 it just stops and the idle animation plays. The character has a NavMeshAgent on it and it looks like the destination reached event is not being triggered when he gets to the waypoint. If anyone has had a situation like this before I would appreciate any information possible. I have been trying to figure out whats wrong for a few hours now and am starting to think it might not be any of the scripts.
here is the waypoint controller
using UnityEngine;
using UnityEngine.AI;
public class WaypointController : MonoBehaviour {
Waypoints[] waypoints;
public Transform target;
//NavMeshPath path;
int currentWaypointIndex = -1;
//private NavMeshAgent agent;
//EnemyCharacter enemy;
public event System.Action<Waypoints> OnWaypointChanged;
// Use this for initialization
void Awake () {
waypoints = GetWaypoints();
}
public void SetNextWaypoint() {
if (currentWaypointIndex != waypoints.Length)
currentWaypointIndex++;
if (currentWaypointIndex == waypoints.Length)
currentWaypointIndex = 0;
if (OnWaypointChanged != null)
OnWaypointChanged(waypoints[currentWaypointIndex]);
//Debug.Log("OnWaypointChanged == null: " + (OnWaypointChanged == null));
//Debug.Log("OnWaypointChanged != null: " + (OnWaypointChanged != null));
}
Waypoints[] GetWaypoints()
{
return GetComponentsInChildren<Waypoints>();
}
private void OnDrawGizmos()
{
Gizmos.color = Color.red;
Vector3 previousWaypoint = Vector3.zero;
foreach (var waypoint in GetWaypoints())
{
Vector3 waypointPosition = waypoint.transform.position;
Gizmos.DrawWireSphere(waypointPosition, .2f);
if (previousWaypoint != Vector3.zero)
Gizmos.DrawLine(previousWaypoint, waypointPosition);
previousWaypoint = waypointPosition;
}
}
}
Here is the EnemyPatrolPoints script
using UnityEngine;
[RequireComponent(typeof(AI_PathFinder))]
public class EnemyPatrolPoints : MonoBehaviour {
[SerializeField]
WaypointController waypointController;
[SerializeField]
float waitTimeMin;
[SerializeField]
float waitTimeMax;
AI_PathFinder pathfinder;
private void Start()
{
waypointController.SetNextWaypoint();
}
private void Awake()
{
pathfinder = GetComponent<AI_PathFinder>();
pathfinder.OnDestinationReached += Pathfinder_OnDestinationReached;
waypointController.OnWaypointChanged += WaypointController_OnWaypointChanged;
}
private void WaypointController_OnWaypointChanged(Waypoints waypoint)
{
pathfinder.SetTarget(waypoint.transform.position);
print("waypoint changed");
}
private void Pathfinder_OnDestinationReached()
{
SealForce_GameManager.Instance.Timer.Add(waypointController.SetNextWaypoint, Random.Range(waitTimeMin, waitTimeMax));
print("destination reached");
}
}
Here is the AI Pathfinder script`
using UnityEngine;
using UnityEngine.AI;
[RequireComponent(typeof(NavMeshAgent))]
public class AI_PathFinder : MonoBehaviour
{
[HideInInspector]
public NavMeshAgent agent;
public EnemyPatrolPoints enemyPatrolPoints;
[SerializeField] float distanceRemainingThreshold;
bool m_destinationReached;
bool destinationReached
{
get
{ return m_destinationReached; }
set
{
m_destinationReached = value;
if (m_destinationReached)
{
if (OnDestinationReached != null)
OnDestinationReached();
}
}
}
public event System.Action OnDestinationReached;
void Start()
{
agent = GetComponent<NavMeshAgent>();
//enemyPatrolPoints = GetComponent<EnemyPatrolPoints>();
}
public void SetTarget(Vector3 target)
{
agent.SetDestination(target);
}
void Update()
{
if (destinationReached)
return;
if (agent.remainingDistance < distanceRemainingThreshold)
destinationReached = true;
}
}
The lines
if (agent.remainingDistance < distanceRemainingThreshold)
destinationReached = true;
are never reached as long as destinationReached is true because of
if (destinationReached)
return;
You are setting it to true after the first waypoint is reached and then never reset it to false so your Update is always skipped in the future.
You should add it e.g. to
public void SetTarget(Vector3 target)
{
agent.SetDestination(target);
destinationReached = false;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Teleport : MonoBehaviour {
public GameObject player;
public Camera mainCamera;
public Camera firstCam;
public Camera camera;
private List<GameObject> TeleportBooths;
private TeleportationsCore tc;
private void Start()
{
InstantiateObjects gos = GetComponent<InstantiateObjects>();
TeleportBooths = new List<GameObject>();
TeleportBooths = gos.PrefabsList();
firstCam.enabled = false;
mainCamera.enabled = false;
camera.enabled = true;
for (int i = 0; i < TeleportBooths.Count; i++)
{
TeleportBooths[i].AddComponent<TeleportationsCore>();
}
tc = GetComponent<TeleportationsCore>();
WorkingBooth();
}
private void WorkingBooth()
{
player.transform.position = TeleportBooths[tc.WorkingBooth()].transform.position;
camera.transform.position = new Vector3(TeleportBooths[tc.WorkingBooth()].transform.position.x - 10, TeleportBooths[tc.WorkingBooth()].transform.position.y + 10, TeleportBooths[tc.WorkingBooth()].transform.position.z);
camera.transform.LookAt(TeleportBooths[tc.WorkingBooth()].transform);
}
private void Update()
{
WorkingBooth();
}
}
I'm doing:
tc = GetComponent<TeleportationsCore>();
But tc is null.
And the script i want to access to:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TeleportationsCore : MonoBehaviour
{
public float spinSpeed = 2.0f;
private bool rotate = false;
private bool exited = false;
private int boothIndex = 0;
private void Start()
{
WorkingBooth();
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player")
{
Debug.Log("Player entered the hole");
rotate = true;
}
}
private void OnTriggerExit(Collider other)
{
if (other.gameObject.tag == "Player")
{
Debug.Log("Player exited the hole");
rotate = false;
exited = true;
}
}
void Rotate()
{
if (rotate)
{
if (spinSpeed == 350)
{
rotate = false;
exited = true;
boothIndex++;
WorkingBooth();
}
else
{
transform.Rotate(Vector3.up, spinSpeed * Time.deltaTime);
spinSpeed += 1f;
}
}
if (rotate == false && exited == true)
{
transform.Rotate(Vector3.up, spinSpeed * Time.deltaTime);
if (spinSpeed > 0.0f)
spinSpeed -= 1f;
}
}
public int WorkingBooth()
{
return boothIndex;
}
private void Update()
{
Rotate();
}
}
What i want is after i attach the script to all gameobject to get access the function WorkingBooth on TeleportationsCore.
And i don't want to attach the TeleportationsCore to the GameObject Teleport is attached on. So what other ways i have to access the WorkingBooth on TeleportationsCore ? Making the WorkingBooth public static ?
Change this:
for (int i = 0; i < TeleportBooths.Count; i++) {
TeleportBooths[i].AddComponent<TeleportationsCore>();
}
to:
TeleportationsCore[] tCores = TeleportBooths.Select(booth => booth.AddComponent<TeleportationsCore>());
Now just pick the core you want from the list.
Attach the TeleportationsCore script to empty game object. To get its reference, use this:
TeleportationsCore core = FindObjectOfType<TeleportationsCore>();
Use it for example in the Start function, as it is a bit slow.
You can find more in documentation.