I am currently developing an AR application in Unity using ARCore for Android devices where I am using following c# script on a indicator cube to showcase current position of the user on a mini map
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using TMPro;
public class SetNavigationTarget : MonoBehaviour
{
[SerializeField]
private TMP_Dropdown navigationTargetDropDown;
[SerializeField]
private List<Target> navigationTargetObjects = new List<Target>();
private NavMeshPath path; // current calculated path
private LineRenderer line;// linerenderer to display path
private Vector3 targetPosition = Vector3.zero; // current target position
private bool lineToggle = false;
private float elapsed = 0.0f;
private void Start()
{
path = new NavMeshPath();
line = transform.GetComponent<LineRenderer>();
line.enabled = lineToggle;
elapsed = 0.0f;
}
private void Update()
{
elapsed += Time.deltaTime;
if (lineToggle && targetPosition != Vector3.zero)
{
if(elapsed > 0.5f)
{
elapsed -= 0.5f;
NavMesh.CalculatePath(transform.position, targetPosition, NavMesh.AllAreas, path);
line.positionCount = path.corners.Length;
line.SetPositions(path.corners);
}
// for (int i = 0; i < path.corners.Length - 1; i++)
// Debug.DrawLine(path.corners[i], path.corners[i + 1], Color.red);
}
}
public void SetCurrentNavigationTarget(int selectedValue)
{
targetPosition = Vector3.zero;
string selectedText = navigationTargetDropDown.options[selectedValue].text;
Target currentTarget = navigationTargetObjects.Find(x => x.Name.Equals(selectedText));
if(currentTarget != null)
{
targetPosition = currentTarget.PositionObject.transform.position;
}
}
public void ToggleVisibility()
{
lineToggle = !lineToggle;
line.enabled = lineToggle;
}
}
but after moving certain distance the cube position is getting reset to few feet behind this error is not consistent but happening quite frequently
These are the error messages I managed to find from the android logcat
2023-01-28 11:06:04.224 7591 7703 Error native E0000 00:00:1674884164.224372 7703 sbr_action_state.cc:220] Failed to obtain consistent ADF to VIO transformation.
2023-01-28 11:06:03.819 7591 7703 Error native E0000 00:00:1674884163.819882 7703 sbr_action_state.cc:85] FAILED_PRECONDITION: Re-evaluation failed and the number of corrupted adf transform VIO frames does not reach the reset threshold.
2023-01-28 11:05:46.433 7591 7703 Error native E0000 00:00:1674884146.433859 7703 sbr_action_state.cc:220] 2 consistent ADF to VIO transformation from MLE are required to initialize this variable in VIO. Current value is 1.
Any help is appreciated
Related
The first script (JumpFromMicrophone) I have created is a script where based on my MICROPHONE audio input, the game object (player) moves up on the Y axis. The game object also automatically moves forward using speed on the Z axis.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class JumpFromMicrophone : MonoBehaviour
{
public AudioSource source;
public AudioLoudnessDetection detector;
public Vector3 minPosition;
public Vector3 maxPosition;
public float speed = 2;
public float loudnessSensibility = 100;
public float threshold = 0.1f;
// The duration of the smooth transition
public float smoothTime = 0.1f;
private Vector3 velocity = Vector3.zero;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void FixedUpdate()
{
float loudness = detector.GetLoudnessFromMicrophone() * loudnessSensibility;
if (loudness < threshold)
loudness = 0;
else
loudness = (loudness - threshold) / (1 - threshold);
// Calculate the target position, keeping the current X and Z values
Vector3 targetPosition = Vector3.Lerp(minPosition, maxPosition, loudness);
targetPosition.x = transform.localPosition.x;
targetPosition.z = transform.localPosition.z;
// Smoothly move the object towards the target position
transform.localPosition = Vector3.SmoothDamp(transform.localPosition, targetPosition, ref velocity, smoothTime);
transform.Translate(Vector3.forward * speed * Time.deltaTime);
}
// reloads the scene if obstacle hits player
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Obstacle")
{
SceneManager.LoadScene("Game");
}
}
}
The second script (RNP) created records this audio picked up by the MICROPHONE and then plays it back to the player using a recording from a generated AudioSource. This is done using MaxRecordingTime, which I am fine using as I am going to relate it to timed game audio once this issue has been resolved.
public class RNP : MonoBehaviour
{
// The AudioClip that will be recorded
public AudioClip recordedClip;
// The maximum recording time in seconds
public int maxRecordingTime = 10;
void Start()
{
// Start the recording and playback coroutine
StartCoroutine(RecordAndPlayback());
}
IEnumerator RecordAndPlayback()
{
// Start recording from the default microphone
recordedClip = Microphone.Start(null, true, maxRecordingTime, 44100);
// Wait for the specified number of seconds
yield return new WaitForSeconds(maxRecordingTime);
// Stop the recording
Microphone.End(null);
// Create an AudioSource to play the recorded audio
AudioSource audioSource = gameObject.AddComponent<AudioSource>();
audioSource.clip = recordedClip;
audioSource.loop = false;
audioSource.Play();
// Wait for the audio to finish playing
yield return new WaitWhile(() => audioSource.isPlaying);
// Destroy the AudioSource
Destroy(audioSource);
}
}
However, the second script seems to clash with the first and implementing the second script does not allow for some aspects of the first script to work, I am beginning to think it might be something to do with the use of the Microphone for both scripts clashing, but I have no idea how to solve this issue so that both work together without disabling one another.
The JumpOnMicrophone script does not allow the player to move up the Y axis when the RNP script is added as a compenent onto the game object (player), but the player still moves along the Z axis.
Some fixes I have attemped that did not work:
Placing the 2nd script on another GameObject rather than the player
Combining both scripts
If anyone could help me figure this out, it would be greatly appreciated.
*** EDIT ***
AudioLoudnessDetection script
public class AudioLoudnessDetection : MonoBehaviour
{
public int sampleWindow = 64;
private AudioClip microphoneClip;
// Start is called before the first frame update
void Start()
{
MicrophoneToAudioClip();
}
// Update is called once per frame
void FixedUpdate()
{
}
public void MicrophoneToAudioClip()
{
string microphoneName = Microphone.devices[0];
microphoneClip = Microphone.Start(microphoneName, true, 20, AudioSettings.outputSampleRate);
}
public float GetLoudnessFromMicrophone()
{
return GetLoudnessFromAudioClip(Microphone.GetPosition(Microphone.devices[0]), microphoneClip);
}
public float GetLoudnessFromAudioClip(int clipPosition,AudioClip clip)
{
int startPosition = clipPosition - sampleWindow;
if (startPosition < 0)
return 0;
float[] waveData = new float[sampleWindow];
clip.GetData(waveData, startPosition);
// compute loudness
float totalLoudness = 0;
for (int i = 0; i < sampleWindow; i++)
{
totalLoudness += Mathf.Abs(waveData[i]);
}
return totalLoudness/ sampleWindow;
}
}
This is my frist time posting here.
I'm using the Mapbox Unity SDK in order to aid the creation of an app I'm developing. At the base of it, we have a 2D top-down map in which we can drag and drop UI images into the map to turn them into geolocalised POIs. I'm using a modified SpawnOnMap script:
using UnityEngine;
using Mapbox.Utils;
using Mapbox.Unity.Map;
using Mapbox.Unity.MeshGeneration.Factories;
using Mapbox.Unity.Utilities;
using System.Collections.Generic;
public class SpawnOnMap : MonoBehaviour
{
[SerializeField]
AbstractMap _map;
[SerializeField]
[Geocode]
string[] _locationStrings;
List<Vector2d> _locations;
public float _spawnScale = 100f;
public GameObject _markerPrefab;
List<GameObject> _spawnedObjects;
void Start()
{
_locations = new List<Vector2d>();
_spawnedObjects = new List<GameObject>();
for (int i = 0; i < _locationStrings.Length; i++)
{
var locationString = _locationStrings[i];
_locations[i] = Conversions.StringToLatLon(locationString);
var instance = Instantiate(_markerPrefab);
instance.transform.localPosition = _map.GeoToWorldPosition(_locations[i], true);
instance.transform.localScale = new Vector3(_spawnScale, _spawnScale, _spawnScale);
_spawnedObjects.Add(instance);
}
}
public void IncreaseMarkers(GameObject inst)
{
_spawnedObjects.Add(inst);
}
public void IncreaseLocations(Vector2d loc)
{
_locations.Add(loc);
}
private void Update()
{
int count = _spawnedObjects.Count;
for (int i = 0; i < count; i++)
{
var spawnedObject = _spawnedObjects[i];
var location = _locations[i];
spawnedObject.transform.localPosition = _map.GeoToWorldPosition(location, true);
spawnedObject.transform.localScale = new Vector3(_spawnScale, _spawnScale, _spawnScale);
}
}
}
and a 'AddPOI' script which is attached to the image:
using UnityEngine;
using Mapbox.Utils;
using Mapbox.Unity.Utilities;
using Mapbox.Unity.Map;
using Mapbox.Examples;
public class AddPOI : MonoBehaviour
{
float clicktime;
Ray ray;
RaycastHit hit;
AbstractMap _map;
[SerializeField]
Transform clickPoint;
[SerializeField]
LayerMask layerMask;
[SerializeField] private SpawnOnMap spawner;
// Start is called before the first frame update
void Start()
{
_map = FindObjectOfType<AbstractMap>();
}
void Update()
{
bool click = false;
if (Input.GetMouseButtonDown(0))
{
clicktime = Time.time;
}
if (Input.GetMouseButtonUp(0))
{
if (Time.time - clicktime < 0.15f)
{
//click = true;
}
}
if (click)
{
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
print(Input.mousePosition);
if (Physics.Raycast(ray, out hit, Mathf.Infinity, layerMask))
{
clickPoint.position = hit.point;
var clickLocation = new Vector2d();
clickLocation = clickPoint.GetGeoPosition(_map.CenterMercator, _map.WorldRelativeScale);
print(clickLocation);
AddPOIOnLocation(clickLocation);
}
}
}
public Vector2d GetLocation()
{
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, Mathf.Infinity, layerMask))
{
clickPoint.position = hit.point;
Vector2d clickLocation = clickPoint.GetGeoPosition(_map.CenterMercator, _map.WorldRelativeScale);
print(clickLocation.x + ", " + clickLocation.y);
return clickLocation;
}
else return Vector2d.zero;
}
public void AddPOIOnLocation(Vector2d location)
{
var instance = Instantiate(spawner._markerPrefab);
instance.transform.localPosition = _map.GeoToWorldPosition(location, true);
instance.transform.localScale = new Vector3(spawner._spawnScale, spawner._spawnScale, spawner._spawnScale);
spawner.IncreaseMarkers(instance);
spawner.IncreaseLocations(location);
}
}
I'm also going from the ZoomableMap example, thus using their pan and zoom script.
Problem is, my script works fine at an angled rotation (the same one present in nearly all other examples, X 35.7 and Y 12.513), but, as soon as I change those angles, the map or camera position, things go haywire. I managed to make it work at a single zoom level and a modified camera position, but otherwise the positions registered in the raycast do not reflect the mouse pointer on the map. The further I go from the original angles, the worse it gets. At one point I just get an insane number that even gets unity to complain about floating point innacuracies. What gives?
Any help is greatly appreciated.
my project is pretty simple. I want to create a 3d object and move it from a tracked Image to another Tracked Image. I managed to do it but i have a problem.
I want my timer to start when both images are tracked.I am new at coding and I couldn't understand if there is any bool type function that is true if my Image is tracked or extended tracked.
I read vuforia's script and api but i couldn't understand how it works.
I want help with this part of my code:
if ( aImage.Status == DefaultObserverEventHandler.TrackingStatusFilter.Tracked_ExtendedTracked && bImage.Status ==DefaultObserverEventHandler.TrackingStatusFilter.Tracked_ExtendedTracked )
My code is down bellow:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lerp : MonoBehaviour
{
[SerializeField] public GameObject cube; //My 3d object
[SerializeField] private Transform a; // Image A position
[SerializeField] private Transform b; //Image B position
private float time = 0;
[SerializeField] public GameObject aImage;
[SerializeField] public GameObject bImage;
void Start()
{
time = 0;
}
private void Update()
{
Vector3 startPosition = a.position;
Vector3 targetPosition = b.position;
if ( aImage.Status == DefaultObserverEventHandler.TrackingStatusFilter.Tracked_ExtendedTracked && bImage.Status ==DefaultObserverEventHandler.TrackingStatusFilter.Tracked_ExtendedTracked )
{
time += Time.deltaTime;
Debug.Log("time:" + time);
cube.transform.position = Vector3.Lerp(startPosition, targetPosition, time / 5f);
if (time == 5f || time > 5f)
{
time = 0;
}
}
}
}
I'm working on an RTS game using mirror and ran into an issue.
I'm working on auto-attack function for the units.
Everything works for the host but not on clients.
Any help would be very much appreciated, I've been at it for a few days already.. Help!
Or at least point me in right direction here.
I will optimize this a bit better later on I just need to understand why it's not working on client. I've also noticed that Client will add itself to enemies list, it seems to be ignoring "hasAuthority" check
It's a bit of a write up so I'll try to make it as understandable as possible.
Unit gets instantiated and this is the script attached to it:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;
using System.Linq;
public class UnitFiring : NetworkBehaviour
{
[SerializeField] private Targeter targeter = null;
[SerializeField] private GameObject projectilePrefab = null;
[SerializeField] private Transform projectileSpawnPoint = null;
[SerializeField] private float fireRange = 10f;
[SerializeField] private float fireRate = 1f;
[SerializeField] private float rotationSpeed = 20f;
private float lastFireTime;
//auto attack
[SerializeField] private Transform ownAimAtPoint = null;
[SerializeField] private LayerMask layerMask;
[SerializeField] private int updateFunctionFrequency = 60; //in frames
[ServerCallback]
private void Update()
{
if (Time.frameCount % this.updateFunctionFrequency != 0) return;
//runs update every 60 frames
enemyColliders.RemoveAll(Collider => Collider == null);
//if enemyCollider List has GameObject that was destroyed or null, it removes it from the list.
Targetable target = targeter.GetTarget();
if(target == null)
{
ArrayDetect();
AttackUnit();
return;
}
if (!CanFireAtTarget()) { return; }
//look at target
Quaternion targetRotation =
Quaternion.LookRotation(target.transform.position - transform.position);
//Rotate
transform.rotation = Quaternion.RotateTowards
(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
if(Time.time > (1 / fireRate) +lastFireTime)
{
Quaternion projectileRotation = Quaternion.LookRotation(
target.GetAimAtPoint().position - projectileSpawnPoint.position);
GameObject projectileInstance = Instantiate(
projectilePrefab, projectileSpawnPoint.position, projectileRotation);
NetworkServer.Spawn(projectileInstance, connectionToClient);
lastFireTime = Time.time;
}
}
[Client]
private bool CanFireAtTarget()
{
return (targeter.GetTarget().transform.position - transform.position).sqrMagnitude
<= fireRange * fireRange;
}
[SerializeField] private Collider[] colliderArray;
[SerializeField] private List<GameObject> enemyColliders;
Next is the ArrayDetect Function
Detect unit with Physics.OverlapSphere, make sure it has the authority and add it to enemyCollider List, I also added layerMask to make sure it's only checking for Units.
[Server]
private void ArrayDetect()
{
colliderArray = Physics.OverlapSphere(ownAimAtPoint.position, fireRange, layerMask);
foreach (Collider collider in colliderArray)
{
Debug.Log("we hit a", collider);
if (!collider.TryGetComponent<Targetable>(out Targetable potentialTarget))
{
return;
}
if (potentialTarget.hasAuthority)
{
return; //if the hit target is the players, do nothing
}
else
{
enemyColliders = enemyColliders.Distinct().ToList();
enemyColliders.Add(collider.gameObject);
Debug.Log("Found an enemy", potentialTarget);
}
}
}
Now AttackUnit Function, it will go thought enemyColliders List and set them as target to attack
[ServerCallback]
private void AttackUnit()
{
foreach (GameObject enemy in enemyColliders)
{
Debug.Log("We got confirmed enemy", enemy);
//GetComponent<Targeter>().CmdSetTarget(enemy);
targeter.CmdSetTarget(enemy);
//attack the enemy
}
}
This is Targetable Script, it simply returns AimAtPoint:
public class Targetable : NetworkBehaviour
{
[SerializeField] private Transform aimAtPoint = null;
public Transform GetAimAtPoint()
{
return aimAtPoint;
}
}
This is the CmdSetTarget Command in Targeter Script:
[Command]
public void CmdSetTarget(GameObject targetGameObject)
{
if(!targetGameObject.TryGetComponent<Targetable>(out Targetable newTarget)) { return; }
//if game object does not have a target, return
target = newTarget;
}
This is the error I get in the console, but only on the client Units, the host runs as it should:
Trying to send command for object without authority. Targeter.CmdSetTarget
Thanks for taking time to read this
When the game starts, a random waypoint is selected from an array. The camera should then rotate to face the selected random waypoint and start moving towards it.
Once the camera has reached the waypoint, it should wait 3 seconds before rotating to face and move towards the next random waypoint.
The problem I have is in Start(). The camera does not rotate to face the first waypoint before it starts moving towards it. Instead, it moves towards the first waypoint backwards. Then, when it reaches the waypoint, it waits 3 seconds to rotate and move towards the next waypoint.
It's working fine except that the camera does not rotate to face the first selected random waypoint. It's moving to it backward without first rotating to face it.
Here's my code:
The waypoints script
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Waypoints : MonoBehaviour
{
public GameObject[] waypoints;
public GameObject player;
public float speed = 5;
public float WPradius = 1;
public LookAtCamera lookAtCam;
private int current = 0;
private bool rot = false;
public void Init()
{
waypoints = GameObject.FindGameObjectsWithTag("Target");
if(waypoints.Length > 0)
{
StartCoroutine(RotateFacingTarget(waypoints[UnityEngine.Random.Range(0, waypoints.Length)].transform));
}
}
void Update()
{
if (waypoints.Length > 0)
{
if (Vector3.Distance(waypoints[current].transform.position, transform.position) < WPradius)
{
current = UnityEngine.Random.Range(0, waypoints.Length);
rot = false;
StartCoroutine(RotateFacingTarget(waypoints[current].transform));
if (current >= waypoints.Length)
{
current = 0;
}
}
if (rot)
transform.position = Vector3.MoveTowards(transform.position, waypoints[current].transform.position, Time.deltaTime * speed);
}
}
IEnumerator RotateFacingTarget(Transform target)
{
yield return new WaitForSeconds(3);
lookAtCam.target = target;
rot = true;
}
}
The look at camera script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LookAtCamera : MonoBehaviour
{
//values that will be set in the Inspector
public Transform target;
public float RotationSpeed;
//values for internal use
private Quaternion _lookRotation;
private Vector3 _direction;
// Update is called once per frame
void Update()
{
//find the vector pointing from our position to the target
if (target)
{
_direction = (target.position - transform.position).normalized;
//create the rotation we need to be in to look at the target
_lookRotation = Quaternion.LookRotation(_direction);
//rotate us over time according to speed until we are in the required rotation
transform.rotation = Quaternion.Slerp(transform.rotation, _lookRotation, Time.deltaTime * RotationSpeed);
}
}
}
How can I fix this?
Let's assume that Waypoints.Init() is being called and your waypoints variable has an array of 3.
Waypoints.Init() starts a coroutine
Your coroutine waits 3 seconds
After 3 seconds, you set your camera target which Slerps to face the position
Update on its first frame says waypoints.Length > 0 == true
It's not close to its target, and rot is false, so it does not move
Now, you're waiting 3 seconds, not rotating, and not moving.
Your coroutine's 3 second wait time is up and starts the rotation toward your target
rot is now true at the start of your rotation, so your Update method starts moving toward the target as well
It would seem that your logic is off in how the order of operations works. If it needs to act as you describe, I suggest that you operate on the target differently.
I've implemented the following using an enum:
Waypoints
public class Waypoints : MonoBehaviour
{
private GameObject[] waypoints;
private Transform currentWaypoint;
private enum CameraState
{
StartRotating,
Rotating,
Moving,
Waiting
}
private CameraState cameraState;
public GameObject player;
public float speed = 5;
public float WPradius = 1;
public LookAtCamera lookAtCam;
private int current = 0;
private bool isCameraRotating = false;
void Start()
{
cameraState = CameraState.StartRotating;
}
void Update()
{
switch (cameraState)
{
// This state is used as a trigger to set the camera target and start rotation
case CameraState.StartRotating:
{
// Sanity check in case the waypoint array was set to length == 0 between states
if (waypoints.Length == 0)
break;
// Tell the camera to start rotating
currentWaypoint = waypoints[UnityEngine.Random.Range(0, waypoints.Length)].transform;
lookAtCam.target = currentWaypoint;
cameraState = CameraState.Rotating;
break;
}
// This state only needs to detect when the camera has completed rotation to start movement
case CameraState.Rotating:
{
if (lookAtCam.IsFinishedRotating)
cameraState = CameraState.StartMoving;
break;
}
case CameraState.Moving:
{
// Move
transform.position = Vector3.MoveTowards(transform.position, currentWaypoint.position, Time.deltaTime * speed);
// Check for the Waiting state
if (Vector3.Distance(currentWaypoint.position, transform.position) < WPradius)
{
// Set to waiting state
cameraState = CameraState.Waiting;
// Call the coroutine to wait once and not in CameraState.Waiting
// Coroutine will set the next state
StartCoroutine(WaitForTimer(3));
}
break;
}
case CameraState.Waiting:
// Do nothing. Timer has already started
break;
}
}
IEnumerator WaitForTimer(float timer)
{
yield return new WaitForSeconds(timer);
cameraState = CameraState.StartRotating;
}
public void RefreshWaypoints()
{
waypoints = GameObject.FindGameObjectsWithTag("Target");
}
}
LookAtCamera
public class LookAtCamera : MonoBehaviour
{
// Values that will be set in the Inspector
public Transform target;
public float RotationSpeed;
private float timer = 0.0f;
public bool IsRotationFinished
{
get { return timer > 0.99f; }
}
// Update is called once per frame
void Update()
{
if (target != null && timer < 0.99f)
{
// Rotate us over time according to speed until we are in the required rotation
transform.rotation = Quaternion.Slerp(transform.rotation,
Quaternion.LookRotation((target.position - transform.position).normalized),
timer);
timer += Time.deltaTime * RotationSpeed;
}
}
}