/
ThirdPersonCamera.cs
239 lines (211 loc) · 8.17 KB
/
ThirdPersonCamera.cs
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class ThirdPersonCamera : MonoBehaviour {
[SerializeField]
private float distanceAway;
[SerializeField]
private float distanceUp;
[SerializeField]
private float smooth;
[SerializeField]
private Transform follow;
[SerializeField]
private float widescreen = 0.2f;
[SerializeField]
private float targetingTime = 0.5f;
[SerializeField]
private float maxDistanceAway;
[SerializeField]
private float minDistanceAway;
[SerializeField]
private GameObject testPosition;
[SerializeField] // just for testing
private Vector3 targetPosition;
private Vector3 lookDir;
private Vector3 velocityCamSmooth = Vector3.zero;
//private float camSmoothDampTime = 0.1f;
private float camSmoothDampTime = 0.2f;
private CamStates camState = CamStates.Behind;
private float defaultDistanceAway = 0f;
private float defaultDistanceUp = 0f;
private List<GameObject> objectsThatShouldAlwaysBeVisible = new List<GameObject>();
// cut scenes
private bool currentlyInCutScene = false;
private GameObject cutSceneTarget;
private float cutSceneTimeLeft;
[SerializeField]
private Vector3 cutSceneOffsetVector;
public float waterSurfaceLevel = 177.5f;
public enum CamStates {
Behind,
Target,
Banking
}
// state-specific configuration
private float defaultBankingCameraYDivisor = 2f;
private float bankingCameraYDivisor;
private float rawHorizontalInputValue = 0f;
void Start () {
follow = GameObject.FindWithTag("Player").transform;
lookDir = follow.forward;
minDistanceAway = distanceAway;
bankingCameraYDivisor = defaultBankingCameraYDivisor;
defaultDistanceUp = distanceUp;
defaultDistanceAway = distanceAway;
}
void LateUpdate() {
if (currentlyInCutScene){
continueToDisplayCutScene();
} else {
performNormalCameraMovement();
}
}
private void continueToDisplayCutScene(){
if (cutSceneTimeLeft > 0f){
cutSceneTimeLeft -= Time.deltaTime;
Vector3 offset = cutSceneTarget.transform.position + cutSceneOffsetVector;
lookDir = offset - this.transform.position;
lookDir.y = 0;
lookDir.Normalize();
targetPosition = offset + cutSceneTarget.transform.up * 0f - lookDir * 0f;
CompensateForWalls(offset, ref targetPosition);
this.transform.position = Vector3.Lerp(this.transform.position, targetPosition, .5f * Time.deltaTime);
//this.transform.position = Vector3.SmoothDamp(this.transform.position, targetPosition, ref velocityCamSmooth, 1f);
transform.LookAt(cutSceneTarget.transform);
} else {
currentlyInCutScene = false;
}
}
private void performNormalCameraMovement(){
captureInputValues();
calculateDesiredDistanceAwayBasedOnFollowers();
Vector3 characterOffset = follow.position + new Vector3(0f, distanceUp, 0f);
// Determine camera state
if (Input.GetAxis("Jump") > 0.01f){
//barEffect.coverage = Mathf.SmoothStep(barEffect.coverage, widescreen, targetingTime);
camState = CamStates.Target;
} else if (rawHorizontalInputValue != 0f || (int)bankingCameraYDivisor != defaultBankingCameraYDivisor){
camState = CamStates.Banking;
} else {
//barEffect.coverage = Mathf.SmoothStep(barEffect.coverage, 0f, targetingTime);
camState = CamStates.Behind;
}
switch(camState){
case CamStates.Behind:
// calculate direction from camera to player, kill y, and normalize to give a valid direction with unit magnitude
lookDir = characterOffset - this.transform.position + (10 * follow.forward);
//lookDir.y = 0;
lookDir.y = lookDir.y / 2f;
lookDir.Normalize();
//Debug.DrawRay(this.transform.position, lookDir, Color.green);
//Debug.DrawLine(follow.position, targetPosition, Color.magenta);
break;
case CamStates.Target:
lookDir = follow.forward;
break;
case CamStates.Banking:
calculateBankingDivisor();
lookDir = follow.forward;
lookDir.y = lookDir.y / bankingCameraYDivisor;
lookDir.Normalize();
break;
}
targetPosition = characterOffset + follow.up * distanceUp - lookDir * distanceAway;
AdjustYValue(ref targetPosition);
CompensateForWalls(characterOffset, ref targetPosition);
smoothPosition(this.transform.position, targetPosition);
transform.LookAt(follow);
}
private void captureInputValues(){
rawHorizontalInputValue = Input.GetAxis("Horizontal");
}
private void calculateBankingDivisor(){
float forwardStep = Time.deltaTime * 1.5f;
float backwardStep = Time.deltaTime * 4f;
if (rawHorizontalInputValue != 0f)
bankingCameraYDivisor = Mathf.SmoothStep(bankingCameraYDivisor, defaultBankingCameraYDivisor + Mathf.Abs(rawHorizontalInputValue), forwardStep);
else
bankingCameraYDivisor = Mathf.SmoothStep(bankingCameraYDivisor, defaultBankingCameraYDivisor, backwardStep);
}
private void AdjustYValue(ref Vector3 toTarget){
float followY = follow.transform.position.y;
if (followY >= (waterSurfaceLevel - 1f)){
if (toTarget.y <= waterSurfaceLevel) toTarget.y += distanceUp;
} else if (followY <= waterSurfaceLevel){
if (toTarget.y >= waterSurfaceLevel) toTarget.y -= distanceUp;
}
}
private void calculateDesiredDistanceAwayBasedOnFollowers(){
if (objectsThatShouldAlwaysBeVisible.Count > 0){
bool zoomOut = false;
float desiredDistance = distanceAway;
foreach(GameObject go in objectsThatShouldAlwaysBeVisible){
float distance = Vector3.Distance(go.transform.position, follow.position);
if ((distance + 7.5f) > desiredDistance) zoomOut = true;
}
desiredDistance += zoomOut == true ? (15f * Time.deltaTime) : (-5f * Time.deltaTime);
distanceAway = (desiredDistance > maxDistanceAway || desiredDistance < minDistanceAway) ? distanceAway : desiredDistance;
} else {
distanceAway = minDistanceAway;
}
}
private void CompensateForWalls(Vector3 fromObject, ref Vector3 toTarget){
Debug.DrawLine(fromObject, toTarget, Color.cyan);
RaycastHit wallHit = new RaycastHit();
if (Physics.Linecast(fromObject, toTarget, out wallHit)) {
string hitTag = wallHit.transform.gameObject.tag;
if (hitTag != "Player" && hitTag != "Fish" && hitTag != "BigTreeRoot" && hitTag != "MainCamera" && hitTag != "CameraShouldIgnore" && !wallHit.transform.collider.isTrigger){ // :|
//Debug.DrawRay(wallHit.point, Vector3.left, Color.red);
//toTarget = new Vector3(wallHit.point.x, toTarget.y, wallHit.point.z);
if (transform.position.y >= waterSurfaceLevel) // shit :| - water surface level... for this level
toTarget = new Vector3(wallHit.point.x, toTarget.y, wallHit.point.z);
else
toTarget = new Vector3(wallHit.point.x, wallHit.point.y, wallHit.point.z); // incorporate Y
}
}
}
private void smoothPosition(Vector3 fromPos, Vector3 toPos){
this.transform.position = Vector3.SmoothDamp(fromPos, toPos, ref velocityCamSmooth, camSmoothDampTime);
}
public string getCamState(){
switch(camState){
case CamStates.Behind:
return "Behind";
case CamStates.Target:
return "Target";
case CamStates.Banking:
return "Banking";
default:
return "Behind";
}
}
public void addObjectThatMustAlwaysRemainInFieldOfView(GameObject theGameObject){
objectsThatShouldAlwaysBeVisible.Add(theGameObject);
}
public void removeObjectThatMustAlwaysRemainInFieldOfView(GameObject theGameObject){
objectsThatShouldAlwaysBeVisible.Remove(theGameObject);
}
public void cutTo(GameObject aGameObject, float timeToWatch, Vector3 offsetVector){
if (!currentlyInCutScene){
currentlyInCutScene = true;
cutSceneTarget = aGameObject;
cutSceneTimeLeft = timeToWatch;
cutSceneOffsetVector = offsetVector;
}
}
public void ResetPosition(){
distanceUp = defaultDistanceUp;
distanceAway = defaultDistanceAway;
}
public void UpdatePosition(float up, float away){
distanceUp = up;
distanceAway = away;
}
public float DistanceAway(){
return distanceAway;
}
public float DistanceUp(){
return distanceUp;
}
}