当前位置:网站首页>Unity 锁定相机第二弹
Unity 锁定相机第二弹
2022-07-30 10:08:00 【一梭键盘任平生】
前段时间 写的战斗相机一旦切换目标过快 相机就太晃了 晃的想吐
然后参考了某个游戏的战斗相机后 做了以下逻辑改动
大概是这样的
1.当每次攻击时锁定最近的目标 找到目标之后 如果目标在一区内则相机不需要转动
2.如果目标在二区内 相机转动到目标出现出现在1区内后停止转动
3.如果目标在屏幕外 相机转动到目标出现在中上区后停止转动
4.停止转动后 如果仅角色移动 相机只跟随角色移动
根据以上需求 优化了部分逻辑
1.添加了一个屏幕位置枚举
private enum TARGETPOSTYPE {
/// <summary>
/// 屏幕中上部分
/// </summary>
MIDDLESCREENTOP,
/// <summary>
/// 屏幕中部(0.45-0.55)
/// </summary>
MIDDLESCREEN,
/// <summary>
/// 屏幕中部(0.25-0.75)
/// </summary>
ONESCREEN,
/// <summary>
/// 屏幕左部(0-0.25)
/// </summary>
TWOSCREENLEFT,
/// <summary>
/// 屏幕右部(0.75-1)
/// </summary>
TWOSCREENRIGHT,
/// <summary>
/// 屏幕外
/// </summary>
OUTSIDESCREEN,
NONE,
}
2.获取目标在屏幕上的位置(0-1)
/// <summary>
/// 获取目标的屏幕位置
/// </summary>
/// <param name="camera"></param>
/// <param name="targetPos"></param>
/// <returns></returns>
public Vector2 GetPosInScreenPos(Camera camera, Vector3 targetPos) {
Vector3 viewPos = camera.WorldToViewportPoint(targetPos);
Vector3 dir = (targetPos - camera.transform.position).normalized;
float dot = Vector3.Dot(camera.transform.forward, dir);
if(dot > 0)
return viewPos;
else
return new Vector2(-1, -1);
}
3.每次攻击确定目标的屏幕位置枚举
/// <summary>
/// 保存每次锁定时锁定目标在屏幕位置
/// </summary>
private void SaveLockTargetScreenType() {
targetShouldType = TARGETPOSTYPE.NONE;
TARGETPOSTYPE targetPosType = GetTargetInScreenType();
switch(targetPosType) {
case TARGETPOSTYPE.TWOSCREENLEFT:
case TARGETPOSTYPE.TWOSCREENRIGHT:
targetShouldType = TARGETPOSTYPE.ONESCREEN;
break;
case TARGETPOSTYPE.OUTSIDESCREEN:
targetShouldType = TARGETPOSTYPE.MIDDLESCREENTOP;
break;
}
}
4.监测目标当前屏幕位置枚举 与 需要到达的屏幕位置枚举
/// <summary>
/// 获取目标的屏幕位置枚举
/// </summary>
/// <param name="target2"></param>
/// <returns></returns>
private TARGETPOSTYPE GetTargetInScreenType() {
Vector2 screenPos = GetPosInScreenPos(camera, target2.position);
Vector2 selfScreenPos = GetPosInScreenPos(camera, target.position);
if(screenPos.x >= 0 && screenPos.x < 0.25f)
return TARGETPOSTYPE.TWOSCREENLEFT;
else if(screenPos.x >= 0.25 && screenPos.x <= 0.75) {
if(screenPos.x >= 0.45 && screenPos.x <= 0.55) {
if(screenPos.y >= selfScreenPos.y)
return TARGETPOSTYPE.MIDDLESCREENTOP;
return TARGETPOSTYPE.MIDDLESCREEN;
} else
return TARGETPOSTYPE.ONESCREEN;
} else if(screenPos.x > 0.75f && screenPos.x <= 1)
return TARGETPOSTYPE.TWOSCREENRIGHT;
else
return TARGETPOSTYPE.OUTSIDESCREEN;
}
/// <summary>
/// 是否停止相机转动
/// </summary>
/// <param name="LockType"></param>
/// <param name="target2"></param>
/// <returns></returns>
private bool StopCameraRot() {
TARGETPOSTYPE targetPosType = GetTargetInScreenType();
if(targetPosType <= targetShouldType) return true;
return false;
}
5.到达位置后改变相机转动逻辑 从战斗相机改成自由相机
if(beginCalcStop && !StopCameraRot()) {
cameraMoveWay1 = false;
} else if(!cameraMoveWay1) {
cameraMoveWay1 = true;
beginCalcStop = false;
}
Vector3 pos = camera.transform.position;
if(cameraMoveWay1) {
CameraMoveWay1(target, pos);
} else
CameraMoveWay2(target, pos);
全部的代码是下面这样的:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class test2 : MonoBehaviour {
#region 锁定相机新逻辑
private enum TARGETPOSTYPE {
/// <summary>
/// 屏幕中上部分
/// </summary>
MIDDLESCREENTOP,
/// <summary>
/// 屏幕中部(0.45-0.55)
/// </summary>
MIDDLESCREEN,
/// <summary>
/// 屏幕中部(0.25-0.75)
/// </summary>
ONESCREEN,
/// <summary>
/// 屏幕左部(0-0.25)
/// </summary>
TWOSCREENLEFT,
/// <summary>
/// 屏幕右部(0.75-1)
/// </summary>
TWOSCREENRIGHT,
/// <summary>
/// 屏幕外
/// </summary>
OUTSIDESCREEN,
NONE,
}
/// <summary>
/// 每次刷新锁定时 确认目标应该去的位置
/// </summary>
private TARGETPOSTYPE targetShouldType;
bool cameraMoveWay1 = false;
bool beginCalcStop = false;
#endregion
/// <summary>
/// 角色
/// </summary>
public Transform target;
/// <summary>
/// 目标
/// </summary>
public Transform target2;
/// <summary>
/// 相机
/// </summary>
public Camera camera;
/// <summary>
/// 相机距离角色水平距离
/// </summary>
public float _radius;
/// <summary>
/// 相机看向角色正前方多少米处
/// </summary>
public float cameraLookDis;
/// <summary>
/// 相机距离角色竖直距离
/// </summary>
public float _camHeight;
/// <summary>
/// 相机旋转角速度变化曲线
/// </summary>
public AnimationCurve _cameraAngleSpeedCurve;
/// <summary>
/// 相机角速度
/// </summary>
public float cameraSpeed;
public bool reset;
// Start is called before the first frame update
void Start() {
}
// Update is called once per frame
void Update() {
if(reset) {
if(beginCalcStop && !StopCameraRot()) {
cameraMoveWay1 = false;
} else if(!cameraMoveWay1) {
cameraMoveWay1 = true;
beginCalcStop = false;
}
Vector3 pos = camera.transform.position;
if(cameraMoveWay1) {
CameraMoveWay1(target, pos);
} else
CameraMoveWay2(target, pos);
}
}
/// <summary>
/// 获取当前角度标准距离后的位置
/// </summary>
/// <returns></returns>
private Vector3 GetDisPos(Vector3 pos, Vector3 _lookat) {
Vector3 view = _lookat - pos;
view.y = 0.0f;
view.Normalize();
Vector3 endPos = _lookat - view * _radius;
endPos.y = 0f;
return endPos;
}
private Vector3 GetCurPos(Vector3 center, Vector3 curPos, float angle) {
return RotateRound(curPos, center, Vector3.up, angle);
}
/// <summary>
/// 围绕某点旋转指定角度
/// </summary>
/// <param name="position">自身坐标</param>
/// <param name="center">旋转中心</param>
/// <param name="axis">围绕旋转轴</param>
/// <param name="angle">旋转角度</param>
/// <returns></returns>
private Vector3 RotateRound(Vector3 position, Vector3 center, Vector3 axis, float angle) {
return Quaternion.AngleAxis(angle, axis) * (position - center) + center;
}
/// <summary>
/// 求出两个向量之间的夹角+-180
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
/// <returns></returns>
public float Angle_180(Vector3 from, Vector3 to) {
from.y = 0; to.y = 0;
Vector3 v3 = Vector3.Cross(from, to);
if(v3.y > 0)
return Vector3.Angle(from, to);
else
return -Vector3.Angle(from, to);
}
/// <summary>
/// 攻击刷新镜头锁定
/// </summary>
/// <param name="target"></param>
public void AttackUpdate() {
SaveLockTargetScreenType();
beginCalcStop = true;
cameraMoveWay1 = false;
}
/// <summary>
/// 获取当前角度标准距离后的位置
/// </summary>
/// <returns></returns>
private Vector3 GetDisPos(Transform target, Vector3 pos, Vector3 _dir) {
Vector3 _lookat = target.position - _dir.normalized * cameraLookDis;
Vector3 view = _lookat - pos;
view.y = 0.0f;
view.Normalize();
Vector3 endPos = _lookat - view * _radius;
endPos.y = 0f;
Debug.DrawLine(target.position, endPos, Color.yellow);
return endPos;
}
#region 锁定相机新逻辑
/// <summary>
/// 转动方式1 保持主角在屏幕中间 不管锁定目标位置
/// </summary>
/// <param name="target"></param>
/// <param name="dir"></param>
/// <param name="pos"></param>
private void CameraMoveWay1(Transform target, Vector3 pos) {
Vector3 dir = target.position - target2.position;
dir.y = 0.0f;
Vector3 lookat = target.position - dir.normalized * cameraLookDis;
//相机看向目标
camera.transform.LookAt(lookat);
Vector3 disEndPos = GetDisPos(target, pos, dir);
Vector3 curPos = GetCurPos(target.position, disEndPos, 0);
//相机位置赋值
camera.transform.position = curPos + Vector3.up * (_camHeight + target.position.y);
}
/// <summary>
/// 转动方式2 保持主角和锁定角色同屏
/// </summary>
/// <param name="target"></param>
/// <param name="dir"></param>
/// <param name="pos"></param>
/// <param name="lookat"></param>
private void CameraMoveWay2(Transform target, Vector3 pos) {
Vector3 dir = target.position - target2.position;
dir.y = 0.0f;
Vector3 lookat = target.position - dir.normalized * cameraLookDis;
//相机看向目标
camera.transform.LookAt(lookat);
//角色到相机的水平方向上的向量
Vector3 roted = dir.normalized * _radius;
//相机应该移动到平面上的位置
Vector3 endPos = lookat + roted;
//当前相机在校准和角色的水平距离后在平面上的位置
Vector3 disEndPos = GetDisPos(pos, lookat);
Vector3 curPos = endPos;
//当前位置和应到位置的的角度(+-180)
float angle = Angle_180(disEndPos - lookat, endPos - lookat);
//根据角度获取曲线上的速度倍率
float curSpeedRate = _cameraAngleSpeedCurve.Evaluate(Mathf.Abs(angle));
float curSpeed = cameraSpeed;
//根据角度调整旋转方向
if(angle > 0) curSpeed *= 1;
else if(angle < 0) curSpeed *= -1;
//根据角度调整位置
if(Mathf.Abs(angle) > Mathf.Abs(curSpeed * Time.deltaTime * curSpeedRate)) {
float rotAngle = curSpeed * Time.deltaTime * curSpeedRate;
//根据圆心角度半径求出旋转角度后应到的位置
curPos = GetCurPos(target.position, disEndPos, rotAngle);
}
//相机位置赋值
camera.transform.position = curPos + Vector3.up * (_camHeight + target.position.y);
}
/// <summary>
/// 保存每次锁定时锁定目标在屏幕位置
/// </summary>
private void SaveLockTargetScreenType() {
targetShouldType = TARGETPOSTYPE.NONE;
TARGETPOSTYPE targetPosType = GetTargetInScreenType();
switch(targetPosType) {
case TARGETPOSTYPE.TWOSCREENLEFT:
case TARGETPOSTYPE.TWOSCREENRIGHT:
targetShouldType = TARGETPOSTYPE.ONESCREEN;
break;
case TARGETPOSTYPE.OUTSIDESCREEN:
targetShouldType = TARGETPOSTYPE.MIDDLESCREENTOP;
break;
}
}
/// <summary>
/// 获取目标的屏幕位置枚举
/// </summary>
/// <param name="target2"></param>
/// <returns></returns>
private TARGETPOSTYPE GetTargetInScreenType() {
Vector2 screenPos = GetPosInScreenPos(camera, target2.position);
Vector2 selfScreenPos = GetPosInScreenPos(camera, target.position);
if(screenPos.x >= 0 && screenPos.x < 0.25f)
return TARGETPOSTYPE.TWOSCREENLEFT;
else if(screenPos.x >= 0.25 && screenPos.x <= 0.75) {
if(screenPos.x >= 0.45 && screenPos.x <= 0.55) {
if(screenPos.y >= selfScreenPos.y)
return TARGETPOSTYPE.MIDDLESCREENTOP;
return TARGETPOSTYPE.MIDDLESCREEN;
} else
return TARGETPOSTYPE.ONESCREEN;
} else if(screenPos.x > 0.75f && screenPos.x <= 1)
return TARGETPOSTYPE.TWOSCREENRIGHT;
else
return TARGETPOSTYPE.OUTSIDESCREEN;
}
/// <summary>
/// 是否停止相机转动
/// </summary>
/// <param name="LockType"></param>
/// <param name="target2"></param>
/// <returns></returns>
private bool StopCameraRot() {
TARGETPOSTYPE targetPosType = GetTargetInScreenType();
if(targetPosType <= targetShouldType) return true;
return false;
}
/// <summary>
/// 获取目标的屏幕位置
/// </summary>
/// <param name="camera"></param>
/// <param name="targetPos"></param>
/// <returns></returns>
public Vector2 GetPosInScreenPos(Camera camera, Vector3 targetPos) {
Vector3 viewPos = camera.WorldToViewportPoint(targetPos);
Vector3 dir = (targetPos - camera.transform.position).normalized;
float dot = Vector3.Dot(camera.transform.forward, dir);
if(dot > 0)
return viewPos;
else
return new Vector2(-1, -1);
}
#endregion
}
攻击的时候触发AttackUpdate方法就会按照规则执行
每个rpg游戏必然有着自己的战斗相机特殊要求 ,这个只是记录自己的经历,程序不易,且行且珍惜
边栏推荐
- Re15: Read the paper LEVEN: A Large-Scale Chinese Legal Event Detection Dataset
- 【HarmonyOS】【ARK UI】HarmonyOS ets语言怎么实现双击返回键退出
- (Text) Frameless button settings
- Understanding of deadlock
- WebAPI 复习
- Flink_CDC construction and simple use
- 分页 paging
- 从数据流中快速查找中位数
- Do you really understand the 5 basic data structures of Redis?
- Re17: Read the paper Challenges for Information Extraction from Dialogue in Criminal Law
猜你喜欢
Re21:读论文 MSJudge Legal Judgment Prediction with Multi-Stage Case Representation Learning in the Real
Re21: Read the paper MSJudge Legal Judgment Prediction with Multi-Stage Case Representation Learning in the Real
多线程保证单个线程开启事务并生效的方案
Flink_CDC搭建及简单使用
Re17: Read the paper Challenges for Information Extraction from Dialogue in Criminal Law
Redis Desktop Manager 2022.4.2 released
Re16: Read the paper ILDC for CJPE: Indian Legal Documents Corpus for Court Judgment Prediction and Explanation
Matplotlib--plot markers
Multi-threading scheme to ensure that a single thread opens a transaction and takes effect
第1章 Kali与靶机系统
随机推荐
Shell system learning function
In 2022, the top will be accepted cca shut the list
Beijing suddenly announced big news in the Metaverse
【HMS core】【FAQ】HMS Toolkit典型问题合集1
In the robot industry professionals, Mr Robot industry current situation?
Neural Ordinary Differential Equations
Redis Desktop Manager 2022.4.2 released
Flink_CDC construction and simple use
Do you really understand the 5 basic data structures of Redis?
[Qualcomm][Network] 网络拨号失败和netmgrd服务分析
Flask's routing (app.route) detailed
Study Notes 11--Direct Construction of Local Trajectories
nacos实战项目中的配置
Determine whether a tree is a complete binary tree - video explanation!!!
Domino服务器SSL证书安装指南
[100 Solidity Skills] 1. Contract reentrancy attack
Re20:读论文的先例:普通法的信息理论分析
关于verilog的时延研究
阿里云OSS对象存储
Domino Server SSL Certificate Installation Guide