Well, after spending most of the day on this, here’s what I came up with:
I like 80% of it; specifically how you can walk around a bit without the camera moving, and also how the camera can move between trees. So far I know of 2 bugs: If the view to the player is blocked by a tree farther than the TreeDodgeDistance away, the camera won’t move. Also, the player can sometimes move out of view to the sides and can only be seen after letting go of the movement keys.
It usually avoids trees, but leaves the small ones alone so the player can still be behind them without the camera moving. I really like that effect and apart from a few bugs, I’m pretty satisfied with the result. I think I need to increase the TreeDodgeDistance so it’ll try to avoid trees farther away, and maybe scale the gigantic ones down slightly.
Also, this was my 1st time using a Tween engine (LeanTween), and I have to say it’s pretty awesome :). I might still look into Cinemachine just to learn it though.
Here’s the rough code, over the next few days, once I have a mostly finished sample I’ll update the code to a polished and well-commented version:
//NOTE: REQUIRES LEANTWEEN. Get it from the Asset Store!
using cakeslice;
using System.Collections;
using UnityEngine;
public class CameraControllerTweenV2 : MonoBehaviour
{
#region "Variables"
public PlayerController PlayerControllerScript;
LeanTween TweenScript;
LeanTweenType TweenType;
public Transform Player;
public Vector3 StartOffset;
public Vector3 PlayerToCameraDifference;
public float OffsetToKeepX;
public float OffsetToKeepZ;
public float MoveTime;
public Vector3 PreviousFrameVector;
public Vector3 DifferecePerFrame;
public Vector3 ToPlayer;
private Ray _Ray;
private RaycastHit _RayHit;
public bool CoolingDownX;
public bool CoolingDownZ;
public bool EnableDirectControlMaster;
public bool EnableDirectControlX;
public bool EnableDirectControlZ;
public bool DodgingTree;
public bool Override;
public bool Test;
public float RayHitDistance;
public bool PlayerIsVisible;
public float TreeDodgeDistance;
#endregion
void Start()
{
StartOffset = transform.position - Player.position;
}
void FixedUpdate()
{
PlayerToCameraDifference = transform.position - Player.position;
if (Mathf.Abs(PlayerToCameraDifference.x) > 5f && !CoolingDownX && !EnableDirectControlX)
{
LeanTween.cancelAll();
EnableDirectControlMaster = true;
EnableDirectControlX = true;
OffsetToKeepX = transform.position.x - Player.position.x;
}
if (Mathf.Abs(PlayerToCameraDifference.z) < 4f && !CoolingDownZ && !EnableDirectControlZ)
PrepareZ();
if (Mathf.Abs(PlayerToCameraDifference.z) > 12f && !CoolingDownZ && !EnableDirectControlZ)
PrepareZ();
if (EnableDirectControlMaster)
{
if (Mathf.Abs(PlayerToCameraDifference.x) > 5f && EnableDirectControlX)
{
//if (TestMovePointForTree(new Vector3((Player.position.x + OffsetToKeepX), transform.position.y, transform.position.z)))
transform.position = new Vector3((Player.position.x + OffsetToKeepX), transform.position.y, transform.position.z);
}
if (Mathf.Abs(PlayerToCameraDifference.z) < 4f || Mathf.Abs(PlayerToCameraDifference.z) > 12f && EnableDirectControlZ)
{
//if (TestMovePointForTree(new Vector3(transform.position.x, transform.position.y, (Player.position.z + OffsetToKeepZ))))
transform.position = new Vector3(transform.position.x, transform.position.y, (Player.position.z + OffsetToKeepZ));
}
//if stopped moving
if (!PlayerControllerScript.IsMoving && !Override)
{
AttemptToCenter();
}
}
ToPlayer = (Player.position - transform.position).normalized;
#region "Raycasting"
if (Physics.Raycast(transform.position, ToPlayer, out _RayHit) && _RayHit.transform.name != "Player")
{
GetComponent<OutlineEffect>().enabled = true;
PlayerIsVisible = false;
}
else
{
GetComponent<OutlineEffect>().enabled = false;
PlayerIsVisible = true;
}
RayHitDistance = _RayHit.distance;
if (_RayHit.transform.tag == "Choppable" && _RayHit.distance < TreeDodgeDistance && !Override && !PlayerIsVisible)
{
AttemptToCenter();
Override = true;
LeanTween.cancelAll();
//DodgingTree = true;
if (TestMovePointForTree(new Vector3(_RayHit.transform.position.x + 5, transform.position.y, _RayHit.transform.position.z)) && _RayHit.transform.name != "Player")
{
StartLerp(new Vector3(_RayHit.transform.position.x + 5, transform.position.y, _RayHit.transform.position.z));
Debug.Log("Dodging right " + _RayHit.transform.name + " " + new Vector3(_RayHit.transform.position.x + 5, transform.position.y, _RayHit.transform.position.z));
return;
}
if (TestMovePointForTree(new Vector3(_RayHit.transform.position.x - 5, transform.position.y, _RayHit.transform.position.z)) && _RayHit.transform.name != "Player")
{
StartLerp(new Vector3(_RayHit.transform.position.x - 5, transform.position.y, _RayHit.transform.position.z));
Debug.Log("Dodging right " + _RayHit.transform.name + " " + new Vector3(_RayHit.transform.position.x - 5, transform.position.y, _RayHit.transform.position.z));
return;
}
else
{
Debug.Log("Can't dodge this tree because " + new Vector3(_RayHit.transform.position.x + 5, transform.position.y, _RayHit.transform.position.z) + " && "
+ new Vector3(_RayHit.transform.position.x - 5, transform.position.y, _RayHit.transform.position.z) + " don't pass tree bool inspection");
Override = false;
}
}
else
Override = false;
#endregion
}
//when dodging a tree, try this FIRST. That way, you don't have to move to an OK position then immediatly center the camera
//because centered was also an OK position
void AttemptToCenter()
{
if (TestMovePointForTree(new Vector3(Player.position.x, transform.position.y, Player.position.z + StartOffset.z)))
{
Debug.Log("Attempted to center, Override no longer needed");
LeanTween.cancelAll();
Override = false;
StopLerp();
}
else
Debug.Log("Stopped centering due to tree near " + new Vector3(Player.position.x, transform.position.y, Player.position.z + StartOffset.z));
}
bool TestMovePointForTree(Vector3 TargetPosition)
{
//Debug.Log("Testing: " + TargetPosition);
if (Physics.Raycast(TargetPosition, (Player.position - TargetPosition).normalized, out _RayHit) && _RayHit.transform.name != "Player")
{
if (_RayHit.transform.tag == "Choppable" && _RayHit.distance < TreeDodgeDistance)
{
Debug.Log(_RayHit.transform.name + " was in the way!");
return false;
}
else
{
Debug.Log(TargetPosition + " was deemed a safe place for the camera");
return true;
}
}
else
{
//Debug.Log("No ray hit from " + TargetPosition);
return true;
}
}
void PrepareZ()
{
LeanTween.cancelAll();
EnableDirectControlMaster = true;
EnableDirectControlZ = true;
OffsetToKeepZ = transform.position.z - Player.position.z;
//OffsetToKeepZ = Player.position.z - 7;
}
void StartLerp(Vector3 Destination)
{
float MoveTime = 3f;
if (Override)
{
MoveTime = 5f;
}
LeanTween.moveX(gameObject, Destination.x, MoveTime).setEase(TweenType);
//need start offset for Z so camera won't be directly above player
if(Override)
LeanTween.moveZ(gameObject, Destination.z, MoveTime).setEase(TweenType);
else
LeanTween.moveZ(gameObject, Destination.z + StartOffset.z, MoveTime).setEase(TweenType);
}
void StopLerp()
{
TweenType = LeanTweenType.easeOutBack;
if(!Override)
StartLerp(Player.position);
EnableDirectControlMaster = false;
EnableDirectControlX = false;
EnableDirectControlZ = false;
}
IEnumerator CoolDownXTimer()
{
CoolingDownX = true;
yield return new WaitForSeconds(0.2f);
CoolingDownX = false;
}
IEnumerator CoolDownZTimer()
{
CoolingDownZ = true;
yield return new WaitForSeconds(0.2f);
CoolingDownZ = false;
}
}
What do you think?