//***********************************************************
// 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) {
}
}