//*********************************************************** // Filename: TargetManager.cs // Author: Niclas // Last changes: 08 August 2018 // Content: This file generates new targets with several parameters //*********************************************************** using System.Collections; using System.Collections.Generic; using UnityEngine; /// /// This class is responsible for generating new targets with various constrains on button press and tracking the experiment progress /// public class TargetManager : MonoBehaviour, IButton { // true if the experiment is ongoing public bool isExpActive = false; // Area in which new targets can be generated private GameObject area; // Bounds of the area private Bounds areaBounds; // The number of targets that will be spawned in this experiment public int numberOfTargets = 0; // Prefab used to generate a new target public GameObject targetPrefab; // The gameobject of prefab instance which is already placed in the scene public GameObject lastTarget; // Constrains for generating the next target // The maximum rotation of the direction of targets in either way public float maxAngle = 10; // minimum and maximum distance of the next target to the current one public float minDistance = 1; public float maxDistance = 3; // The maximum y rotation of the next target public float maxTargetRotation = 360; // Directional Vector from which the direction in which the next target spawns will be calculated private Vector3 dirOfNextPos = Vector3.right; // 0 for starting target, >0 for "true" targets; doubles as target count private int currentTargetId = 0; // Use this for initialization void Start() { // Set base Target which only used to calculate the postion of the first "real" Target if (lastTarget == null) { lastTarget = new GameObject(); } area = GameObject.FindGameObjectWithTag("ExpPlane"); areaBounds = area.GetComponent().mesh.bounds; } // Update is called once per frame void Update() { // Debug Listener for KeyPress if (Input.GetKeyDown(KeyCode.Space)) { OnButtonUp(); } } /// /// Spawns Target at desired position and rotation as child of area /// private void SpawnTarget() { currentTargetId++; GameObject target = Instantiate(targetPrefab); target.transform.SetParent(area.transform, false); target.transform.localPosition = RandomRestrictedPosition(); target.transform.rotation = RandomYRotation(); target.GetComponent().id = currentTargetId; lastTarget = target; // Fire event that a new target has been spawned TargetSpawnedEvent tse = new TargetSpawnedEvent(); tse.targetId = currentTargetId; tse.targetTransform = target.transform; tse.FireEvent(); } /// /// Generates a random rotation with nullvector as base, is separate to allow possible restrictions in rotation /// /// Returns a Quaternion containing a random rotation within constrains private Quaternion RandomYRotation() { Vector3 euler = Vector3.zero; euler.y = Random.Range((-maxTargetRotation / 2) - 90, (maxTargetRotation / 2) - 90); return Quaternion.Euler(euler); } /// /// Returns a randomly selected position in the area and calls for a check if it is valid /// /// Returns a within constrains random and valid position private Vector3 RandomRestrictedPosition() { Vector3 nextPos = lastTarget.transform.localPosition; float rot = Random.Range(-maxAngle, maxAngle); Vector3 moveTarget = Quaternion.AngleAxis(rot, Vector3.up) * dirOfNextPos; // TODO: improve so it rotates based on lastTarget rotation moveTarget = moveTarget.normalized * Random.Range(minDistance, maxDistance); nextPos += moveTarget; nextPos.y = 0; return ValidatePosition(nextPos); } /// /// Checks if next Position is in the areas bounds and set it inside if needed /// /// The position that should be checked if it lies within constrains /// Returns the position with, if necessary, corrections private Vector3 ValidatePosition(Vector3 position) { if (!areaBounds.Contains(position)) { Debug.Log("OutOfBounds! ClosestPos: " + areaBounds.ClosestPoint(position)); position = areaBounds.ClosestPoint(position) + ((areaBounds.ClosestPoint(position) - position).normalized * maxDistance); dirOfNextPos = dirOfNextPos * -1; } return position; } /// /// Called when the corresponding button on the controller this script is put into, is released /// public void OnButtonUp() { if (isExpActive) { Destroy(lastTarget); if (currentTargetId == numberOfTargets) { isExpActive = false; ExperimentEndEvent eee = new ExperimentEndEvent(); eee.FireEvent(); } else { SpawnTarget(); } } } // unused; necessary to match signature public void OnButtonDown(GameObject controllerObject, int controllerIdentificator) { } }