Actor

using System.Collections;
using UnityEngine;
using UnityEngine.AI;public class Actor : MonoBehaviour
{[HideInInspector] public ActorVisualHandler visualHandler;private NavMeshAgent agent;private Animator animator;//角色需要一个当前的攻击目标,可受伤的攻击目标Damageableprivate Damageable damageableTarget;private AnimationEventListener animationEvent;public int attack = 10; //角色攻击力 = 10,如果采集物的生命值=100,就要敲击10次private Coroutine currentTask;public GameObject hitEffect;private void Start(){visualHandler = GetComponent<ActorVisualHandler>();agent = GetComponent<NavMeshAgent>();animator = GetComponentInChildren<Animator>();animationEvent = GetComponentInChildren<AnimationEventListener>();animationEvent.attackEvent.AddListener(Attack); //将Attack作为事件,在动画帧上执行animationEvent.attackEvent.AddListener(AttackEffect);}private void Update(){//Animator是通过人物的速度Speed来切换不同的动画状态片段的animator.SetFloat("Speed", Mathf.Clamp(agent.velocity.magnitude, 0, 1));}//人物的移动, _target表示人物应该移动到的鼠标位置,由于是未知的,所以设置为参数public void SetDestination(Vector3 _target){agent.SetDestination(_target);}public void Attack(){if (damageableTarget)damageableTarget.Hit(attack);}//方法:采集/攻击,这个方法将会在ActorManager脚本中的另一种情况下调用public void AttackTarget(Damageable _target){StopTask(); //这里你要想到一个问题就是,每次采集的都是不同的,所以要忘记上次的任务damageableTarget = _target;//先走到采集物/敌人的附近,再开始采集/攻击currentTask = StartCoroutine(StartAttack());}IEnumerator StartAttack(){//情况1: 有点击到敌人,敌人还活着,能采集,继续采集while (damageableTarget){//首先,我们要走到这个地方啊SetDestination(damageableTarget.transform.position);//那走到啥位置停下来呢?yield return new WaitUntil(() => agent.remainingDistance <= agent.stoppingDistance && !agent.pathPending);while (damageableTarget && Vector3.Distance(damageableTarget.transform.position, transform.position) < 4f){yield return new WaitForSeconds(1); //每间隔1秒攻击一次,如果太短的话可能会在采集物消失后,多一次攻击动画if (damageableTarget){animator.SetTrigger("Attack"); //调用Attack动画,采集物消失后,由于角色速度小于等于0.01,就回到Idle动画片段//Instantiate(hitEffect, damageableTarget.transform.position, Quaternion.identity);//别写在这个地方}}}//情况2: 采集完了,没东西了currentTask = null;}public void StopTask(){damageableTarget = null; //首先,让采集目标为空if (currentTask != null) //停止正在手头的这份工作,即:立即停止协程的执行StopCoroutine(currentTask);}public void AttackEffect(){if (damageableTarget)Instantiate(hitEffect, damageableTarget.transform.position, Quaternion.identity);}
}

ActorManager

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ActorManager : MonoBehaviour
{public static ActorManager instance;//单例模式[SerializeField] private Transform selectedArea;//框选的范围,实际是一个立方体,一会显示框选范围public List<Actor> allActors = new List<Actor>();//场景中所有己方角色[SerializeField] private List<Actor> selectedActors = new List<Actor>();//当前选中的角色(们)//鼠标拖拽的起始点,终点,和计算显示的框选Cube的中心位置和实际大小,用于localScale的实现private Vector3 dragStartPos, dragEndPos, dragCenter, dragSize;public LayerMask mouseDragLayerMask;private bool isDragging;public LayerMask dragSelectedLayerMask;//只框选角色,即BoxCastAll方法中的指定层private void Awake(){if(instance == null){instance = this;}else{if (instance != this)Destroy(gameObject);}DontDestroyOnLoad(gameObject);//防止场景转换时,保持唯一性}private void Start(){selectedArea.gameObject.SetActive(false);//一开始框选是不可见的//所有在场景中的角色,都应该添加到allActors这个List中foreach(Actor actor in GetComponentsInChildren<Actor>()){allActors.Add(actor);//相当于游戏开始后完成了“注册”的工作}}private void Update(){MouseInput();}private void MouseInput(){if(Input.GetMouseButtonDown(0))//按下鼠标「开始拖拽」时,需要存储这个点在【世界坐标系】下的位置信息{Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);//if(Physics.Raycast(ray, out RaycastHit raycastHit, 100, LayerMask.GetMask("Level"))) if (Physics.Raycast(ray, out RaycastHit raycastHit, 100, mouseDragLayerMask)){dragStartPos = raycastHit.point;//raycastHit结构体,out是输出参数,作为方法的第二个输出使用}} else if (Input.GetMouseButton(0))//「按住」鼠标左键的时候{Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);if (Physics.Raycast(ray, out RaycastHit raycastHit, 100, mouseDragLayerMask)){dragEndPos = raycastHit.point;//raycastHit结构体,out是输出参数,作为方法的第二个输出使用}//这里我们希望的是只有拖动了一段距离以后,才会出现框选的范围,而不是细微的变化就会出现框选if(Vector3.Distance(dragStartPos, dragEndPos) > 1){isDragging = true;//正在拖动selectedArea.gameObject.SetActive(true);//框选范围可见//生成一个范围大小和坐标了dragCenter = (dragStartPos + dragEndPos) / 2;//0, 20 -> 10 | -40, 90 -> 25dragSize = dragEndPos - dragStartPos;selectedArea.transform.position = dragCenter;selectedArea.transform.localScale = dragSize + Vector3.up;//提高一点框选可见范围的空中高度} } else if(Input.GetMouseButtonUp(0)){//情况1: 我们之前还在拖动,范围内全选的工作if(isDragging == true){//松开dragSelectedLayerMaskSelectActors();isDragging = false;selectedArea.gameObject.SetActive(false);}else//情况2: 我们之前其实没有在拖动,这时候鼠标松开其实纯碎只是鼠标左键的点击,可能是角色的移动、攻击/采集{SetTask();}}//if(UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject())//{//    isDragging = false;//    selectedArea.gameObject.SetActive(false);//    return;//}}private void SetTask(){//Debug.Log("Set Task!");//首先要注意一点,如果我们框选空气的话,或者随便点击左键,触发这个方法都不该有什么逻辑执行if (selectedActors.Count == 0)return;Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);Collider collider;if(Physics.Raycast(ray, out RaycastHit hitInfo, 100)){collider = hitInfo.collider;//获取射线检测到的这个点的Collider组件//如果射线检测到的,鼠标点的这个玩意是「地形」的话,移动到这个位置if(collider.CompareTag("Terrain")){foreach(Actor actor in selectedActors){actor.StopTask();Ray _ray = Camera.main.ScreenPointToRay(Input.mousePosition);if(Physics.Raycast(_ray, out RaycastHit raycast, 100, mouseDragLayerMask)){Vector3 _targetPos = raycast.point;actor.SetDestination(_targetPos);}}}//如果射线检测到的,鼠标点的这个玩意是「石头/敌人/马车」的话,采集/攻击else if(!collider.CompareTag("Player")){Damageable damageable = collider.GetComponent<Damageable>();//获取到点击的这个含collider组件的游戏对象if(damageable != null){foreach(Actor actor in selectedActors){actor.AttackTarget(damageable);//满足条件的选中角色,向鼠标点击的这个含Damageable脚本的游戏对象,调用AttackTarget方法}}}}}private void SelectActors(){DeselectActors();//每次框选其实都是重新选择,所以需要删去上一次的所有选中的角色//Debug.Log("We have Selected Actors!!!!!!");//dragSize = new Vector3(Mathf.Abs(dragSize.x), 1f, Mathf.Abs(dragSize.z / 2));//这里是Z在3D世界dragSize.Set(Mathf.Abs(dragSize.x / 2), 1f, Mathf.Abs(dragSize.z / 2));//HalfExtentRaycastHit[] hits = Physics.BoxCastAll(dragCenter, dragSize, Vector3.up, Quaternion.identity, 0, dragSelectedLayerMask);foreach(RaycastHit hit in hits){Actor actor = hit.collider.GetComponent<Actor>();//我们要检测,框选到的是不是Actor角色if(actor != null){selectedActors.Add(actor);//将选中的角色,添加到selectedActor这个List中actor.visualHandler.Select();}}}private void DeselectActors(){foreach(Actor actor in selectedActors){actor.visualHandler.Deselect();//将之前所有已经选中的Actors的下标光圈关闭}selectedActors.Clear();//Clear删除之前SelectedActors这个List中的所有元素}}

ActorVisualHandler

using DG.Tweening;
using UnityEngine;public class ActorVisualHandler : MonoBehaviour
{public SpriteRenderer spriteRenderer;public void Select(){spriteRenderer.enabled = true; //开启spriteRenderer.transform.DOScale(0, 0.35f).From().SetEase(Ease.OutBack);}public void Deselect(){spriteRenderer.enabled = false; //SR组件关闭}
}

AnimationEventListener

using UnityEngine;
using UnityEngine.Events;public class AnimationEventListener : MonoBehaviour
{[HideInInspector] public UnityEvent footStepEvent = new UnityEvent();[HideInInspector] public UnityEvent attackEvent = new UnityEvent();public void FootstepEvent(){footStepEvent.Invoke();}public void AttackEvent(){attackEvent.Invoke();}
}

CameraController

using UnityEngine;public class CameraController : MonoBehaviour
{[SerializeField] Vector2 zoomLimits;[SerializeField] float cameraSpeed;Transform mainCamera;Transform zoomObject;Vector2 input;bool mouseMove = true;Vector3 moveInput;void Awake(){mainCamera = Camera.main.transform;transform.LookAt(mainCamera);zoomObject = transform.GetChild(0);}void Update(){moveInput.Set(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0);if (mouseMove){Vector2 mousePos = Input.mousePosition;if (mousePos.x > Screen.width * 0.95f && mousePos.x < Screen.width)moveInput.x = 1;else if (mousePos.x < Screen.width * 0.05f && mousePos.x > 0)moveInput.x = -1;if (mousePos.y > Screen.height * 0.95f && mousePos.y < Screen.height)moveInput.z = 1;else if (mousePos.y < Screen.height * 0.05f && mousePos.y > 0)moveInput.z = -1;}Vector3 movementDirection = mainCamera.TransformDirection(moveInput);movementDirection.y = 0;transform.position += movementDirection.normalized * Time.deltaTime * cameraSpeed;zoomObject.localPosition += new Vector3(0, 0, -Input.mouseScrollDelta.y);zoomObject.localPosition = new Vector3(zoomObject.localPosition.x, zoomObject.localPosition.y,Mathf.Clamp(zoomObject.localPosition.z, zoomLimits.x, zoomLimits.y));}
}

Damageable

using System;
using UnityEngine;//通常情况下,这里我会使用接口
//这个案例中这个脚本会涉及字段、事件、方法,所以使用简单的类
public class Damageable : MonoBehaviour
{[SerializeField] private int maxHp = 100;private int currentHp;//不去使用案例中的UnityEventpublic event Action OnHit; //只需要+=添加事件就行了public event Action OnDestroy;private void Start(){currentHp = maxHp;}public void Hit(int _damageAmount){OnHit(); //OnHit.Invoke();currentHp -= _damageAmount;if (currentHp <= 0){OnDestroy(); //调用事件Destroy(gameObject);}}
}

DestroyVisual

using UnityEngine;public class DestroyVisual : MonoBehaviour
{private Damageable damageable;public GameObject destroyParticle;private void Start(){damageable = GetComponent<Damageable>();damageable.OnDestroy += DestroyEffect; //事件的添加}private void OnDestroy(){damageable.OnDestroy -= DestroyEffect; //事件的取消订阅,防止内存泄漏}public void DestroyEffect(){Instantiate(destroyParticle, transform.position + Vector3.up, Quaternion.identity);}
}

Resources

using DG.Tweening;
using UnityEngine;public enum Resourcetype
{Wood,Stone
}public class Resources : MonoBehaviour
{[SerializeField] private Resourcetype resourceType;public bool isHovering; //判断采集物是否要高亮[SerializeField] private int bounsAmount;private MeshRenderer meshRenderer;private Color originalColor;private Damageable damageable;private void Start(){meshRenderer = GetComponent<MeshRenderer>();//originalColor = meshRenderer.material.color;//获取的其实只是材质的Base ColororiginalColor = meshRenderer.material.GetColor("_EmissionColor");damageable = GetComponent<Damageable>();damageable.OnHit += HitResources;damageable.OnDestroy += AddResources;}private void OnDestroy(){damageable.OnHit -= HitResources;damageable.OnDestroy -= AddResources; //事件的取消订阅,为了防止出现内存泄漏的问题!}private void Update(){if (Input.GetKeyDown(KeyCode.Space))HitResources(); //仅做测试使用}//这个方法将会+=到OnHit事件上,表示当击中目标后,调用的方法public void HitResources(){//每次击中后,调用一个小动画transform.DOComplete();transform.DOShakeScale(0.5f, 0.2f, 10, 90, true);}//这个方法将会+=到OnDestroy事件上,表示目标阵亡后,调用这个方法public void AddResources(){Debug.Log("Resources : " + resourceType + " increase : " + bounsAmount + " !");}private void OnMouseEnter() //含Collider{isHovering = true;meshRenderer.material.SetColor("_EmissionColor", Color.gray);}private void OnMouseExit(){isHovering = false;meshRenderer.material.SetColor("_EmissionColor", originalColor);}
}

Unity3D 官方案例实现类似红警的移动相关推荐

  1. 【初阶】unity3d官方案例_太空射击SpacingShooter 学习笔记 显示分数时,如何让函数之间相互交流...

    [初阶]unity3d官方案例_太空射击SpacingShooter 学习笔记 显示分数时,如何让函数之间相互交流 一.关于 显示分数时,如何让函数之间相互交流 这是一个非常好的逻辑问题 1 思路:主 ...

  2. Unity3D官方案例1-星际航行游戏Space Shooter

    Unity3D官方案例1-星际航行游戏Space Shooter [1]学习中的使用的类 1.Input:使用此类读取常规游戏设置中的轴,访问移动设备的多点触控和加速度. 本例使用到的方法: GetA ...

  3. unity 彩带粒子_[Unity3D] 官方案例——粒子系统制作火焰效果

    1. 导入资源 打开素材里面的Shuriken场景,然后通过菜单GameObject->Particle System创建一个粒子系统对象,并将物体移至火把位置,此时效果如下: 2. 设置粒子系 ...

  4. [Unity3D]Unity3D官方案例SpaceShooter开发教程

    1.先导入SpaceShooter的环境资源,将Models下的vehicle_playerShip放到Hierachy窗口,改名为Player.Rotation x改为270,为Player添加me ...

  5. Unity3D官方案例 - 2DGame - 帽子接球

    1.创建场景 2.创建Background和Foreground工作层 3.添加静态景物 1.导入静态图片 2.创建静态景物 1.创建sprite:sky 2.设置Position,纹理与层级 Sor ...

  6. Unity3D官方推荐教程roll a bal案例

    Unity3D官方推荐教程roll a bal案例 首先声明本人菜鸟一枚,此博客只是记录一下学习过程,虽说是跟着教程一步一步做的,但是在做的过程当中也遇到了很多问题(菜是原罪),这些问题精良写在了文中 ...

  7. 当局者迷旁观者清红警游戏反杀局案例分析

    粉丝问我:南天可不可以低吸?我没回复: 有朋友向我感叹:股市跌的时候心情不好,涨的时候心情也不好.我没有理会. 对于这波行情走势特别上周五的大涨,我的直观感受是因为 国脉科技和南天信息的超预期,引发踏 ...

  8. 红警 1 游戏开源,代码非常规范,网友:秀色可餐

    作者 | 程序员的那些事 来源 | 程序员的那些事(id:iProgrammer) 最后有一个小测试!测测你是不是红警老玩家! EA 部分开源红警啦! 5 月 27 日,知名游戏公司 EA 在 Git ...

  9. Unity官方案例——Roll a ball

    现在的我还是一名 Unity3d 游戏开发初学者,所以现在都在围绕 Unity3d 的官方案例来做练习,为此写下一些文章作为笔记,文章中难免会有一些疏漏,有些不当之处还望指正. 项目简介 首先玩家可以 ...

  10. Win10的两个实用技巧系列之设置鼠标指针、红警玩不了怎么办?

    win10系统怎么设置鼠标指针在打字时隐藏? win10隐藏鼠标指针的方法 win10系统怎么设置鼠标指针在打字时隐藏?win10系统输入文字的时候,想要隐藏鼠标指针,该怎么操作呢?下面我们就来看看w ...

最新文章

  1. C#中string与byte[]的转换帮助类
  2. pycharm 显示 ini
  3. 前端学习笔记之-VUE框架学习-Vue核心
  4. error_reporting
  5. The Geometry has no Z values 解决办法
  6. 从头认识Spring-3.4 简单的AOP日志实现-扩展添加检查订单功能,以便记录并检測输入的參数...
  7. 漫画:唐玄奘教你横扫 AVL 树面试题无敌手!
  8. 移除元素所有事件监听_前端日记—DOM 事件机制和事件委托
  9. top 内存耗用:VSS/RSS/PSS/USS
  10. 语言概率模型和Word2Vec简述
  11. Android动画插入器
  12. hibernate 数据分页显示 及 分页导航栏的设置
  13. 数据显示:中国程序员是世界上最牛的程序员
  14. W3af详细安装与基本使用
  15. 实战PyQt5: 130-使用HTTP请求下载文件
  16. python画饼图柱状图_荐【python数据分析(24)】Matplotlib库基本图形绘制(1)(线形图、柱状图、堆叠图、面积图、填图、饼图)...
  17. 台式u盘显示计算机未响应怎么办,u盘插上去电脑没反应怎么办_u盘插上后无任何反应的解决教程-系统城...
  18. tomcat HTTP与HTTPS同时开启并且同时可以访问
  19. 英文翻译法语-英文法语翻译软件
  20. CSS进阶(7)- 样式补充

热门文章

  1. 如何在PowerPoint中更改整个演示文稿的格式
  2. DBA必备技能:数据库挂起时进行转储分析诊断案例
  3. 边境的悍匪—机器学习实战:第一章 机器学习的基础知识
  4. 物联网安全风险威胁报告
  5. html mp4断点播放,html5解决大文件断点续传
  6. 灵飞经4 西城八部 第二十章 倒行逆施(五)
  7. element-ui更改图标icon大小
  8. 软件工程第五次作业-项目选题
  9. 设计模式——设计模式中英文对照
  10. seo外链怎么做?网站外链怎么发布