-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLoomingStimulusControlScript.cs
More file actions
97 lines (83 loc) · 3.06 KB
/
Copy pathLoomingStimulusControlScript.cs
File metadata and controls
97 lines (83 loc) · 3.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/*
* Looming Stimulus Control Script
*
* Simulates an approaching predator (overhead threat) using physics-based
* player tracking and interception algorithms. The stimulus spawns at a
* distance and approaches the player with optimal heading calculation.
*
* Scientific Context:
* - Models innate defensive responses to looming threats
* - Based on classic looming stimulus paradigms (Schiff et al., 1962)
* - Used to study predator avoidance in rodents (De Franceschi et al., 2016)
*
* Key Features:
* - Randomized spawn position within elevation/azimuth ranges
* - Physics-based interception (accounts for player velocity)
* - Triggers air puff punishment on collision
* - Automatically deactivates after contact
*/
public class LoomingStimulusControlScript : MonoBehaviour
{
public GameObject player;
public float startDistance = 40f;
public float speed = 1f;
public Vector2 ThRange = new Vector2(25.0f, 30.0f); // Elevation angle range (degrees)
public Vector2 PhRange = new Vector2(-35.0f, 35.0f); // Azimuth angle range (degrees)
float toRad = (2.0f * Mathf.PI)/360.0f;
public void activate()
{
// Calculate random spawn position in spherical coordinates
float Th = Random.Range(ThRange.x, ThRange.y) * toRad;
float Ph = Random.Range(PhRange.x, PhRange.y) * toRad;
// Convert to Cartesian coordinates
Vector3 v = new Vector3(
Mathf.Sin(Ph) * Mathf.Cos(Th),
Mathf.Cos(Ph) * Mathf.Cos(Th),
Mathf.Sin(Th)
);
gameObject.transform.position = player.transform.position + v * startDistance;
gameObject.SetActive(true);
}
public void deactivate()
{
gameObject.SetActive(false);
}
void Update()
{
// Physics-based interception algorithm
// Computes optimal heading to chase moving player
// Direction to player
Vector3 D = player.transform.position - gameObject.transform.position;
D.Normalize();
// Player velocity
Vector3 v = player.GetComponent<CharacterController>().velocity;
float s_player = v.magnitude;
s_player = Mathf.Min(s_player, speed);
v.Normalize();
// Calculate perpendicular velocity component
Vector3 v_perp = v - Vector3.Dot(v, D) * D;
float sin_ph = v_perp.magnitude;
float sin_th = sin_ph * s_player / speed;
v_perp.Normalize();
// Optimal interception velocity
Vector3 w = (Mathf.Sqrt(1 - sin_th * sin_th) * D + sin_th * v) * speed;
gameObject.transform.position += w * Time.deltaTime;
}
private void OnTriggerEnter(Collider c)
{
if(c.gameObject == player)
{
// Deliver punishment on collision
player.GetComponent<PlayerController>().deliverAirpuff();
deactivate();
}
else
{
Debug.Log(c.gameObject);
deactivate();
}
}
}