2019-09-17 20:23:13 weixin_44292962 阅读数 186
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

A*寻路算法(分队自动寻路)


参考:https://www.cnblogs.com/wangweixznu/p/5442164.html

简单描述

在上一次上机的基础下继续进行学习。

操作过程

  1. 建立一个简单的场景,里面包括3条路(用红蓝绿区分)。
    在这里插入图片描述

  2. 设置NavMeshLayer,需要设置Red、Blue和Green三个层。
    在这里插入图片描述在这里插入图片描述

  3. 相应颜色的路, 在Navigation Layer里面选择相应颜色层。

  4. 设置多个不同颜色的AI。
    在这里插入图片描述

  5. 选择蓝色AI,然后找到NavMesh Agent组件里面的NavMesh Walkable选项,这里就是人物能通过的层的选择了。按照刚才层的设计,蓝色的小兵需要勾选Blue层 ,把Red层和Green层取消选择。
    在这里插入图片描述
    其他颜色的AI操作类似。

  6. 检查结果
    在这里插入图片描述

2017-06-13 10:48:22 yongh701 阅读数 1942
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

在《【Unity3D】3D模型的使用——FBX的使用与Animation设置》(点击打开链接)曾经说明了,在Unity3D中使用3D模型,本文将进一步地说明如何用这个3D模型配合《【Unity3D】自动寻路》(点击打开链接)做出游戏主角的移动效果,同时进一步地实现连击。

本文使用的是:http://download.csdn.net/detail/yongh701/9867149中一个英雄模型。

将制作出如下的效果:


点击左键可以自由行走,行走的时候,不会碰到障碍物,然后点击右键可以进行连击,最多可以达到三连击。

制作过程如下:

一、场景布置

1、导入了英雄模型之后,可以看到fighter char采用传统的Animation而并非新型的Animator来实现英雄的动画,我们需要对Animation的一些基本属性进行设置,比如Animation设置为Stand,也就是默认是播放Stand这个动画,之后Size设置为5,输入按回车之后,用Stand、run、punch1~3分别填充Element0~5,意思英雄包含这5个动作。


2、之后场景布置如下:


3、与《【Unity3D】自动寻路》(点击打开链接)同理,保存当前的场景为Hero,对于Cube、Plane这些障碍体设置Navigation Static,然后点击烘培Bake,让Unity3D完成自动寻路的计算。


之后对主角fighter char添加一个Nav Mesh Agent组件,同时设置其半径radius为2,高度Height为8,主要看到绿色的导航圆柱体,能够完全覆盖主角,避免出现穿墙、入墙等现象。


二、脚本编写

对于英雄赋予如下的脚本Hero.cs:

using UnityEngine;
using System.Collections;

public class Hero : MonoBehaviour
{

    public GameObject plane;//地板
    private NavMeshAgent navMeshAgent;//自动寻路,必须要的导航体
    private int attack_combo;//连击数
    private bool is_Move, is_Attack;//是否在移动、攻击的flag

    void Start()//初始化是一个站着的状态
    {
        navMeshAgent = gameObject.GetComponent<NavMeshAgent>();//初始化navMeshAgent  
        attack_combo = 0;//连击数为0
        is_Move = false;//不在移动
        is_Attack = false;//不在攻击
    }

    void Update()
    {

        if (is_Move && navMeshAgent.remainingDistance < 0.01)//如果在移动中,而且离终点的距离不多于0.01
        {
            is_Move = false;//则设置为不移动状态
        }

        if (Input.GetMouseButtonDown(0))//鼠标左键点下    
        {
            animation.Play("run");//开始你的移动
            Ray mRay = Camera.main.ScreenPointToRay(Input.mousePosition);//住摄像机向鼠标位置发射射线   
            RaycastHit mHit;
            if (Physics.Raycast(mRay, out mHit))//射线检验   
            {
                if (mHit.collider.gameObject == plane)
                {
                    navMeshAgent.SetDestination(mHit.point);//mHit.point就是射线和plane的相交点,实为碰撞点   
                    is_Move = true;//设置为移动状态
                }
            }
        }

        if (Input.GetMouseButtonDown(1))//鼠标右键点下  
        {
            navMeshAgent.Stop();//停止移动
            is_Move = false;
            if (attack_combo < 3)//如果连击数没到达上限,则开始连击
            {
                attack_combo++;
            }
            animation.Play("punch" + attack_combo);//进行相应阶段的连击
        }

        /*判断是否正处于攻击状态中,主要是判断是否将连击动画播放完毕*/
        bool is_Attack = false;
        string[] attack_animation_name = { "punch1", "punch2", "punch3" };
        for (int i = 0; i < attack_animation_name.Length; i++)
        {
            if (animation.IsPlaying(attack_animation_name[i]))
            {
                is_Attack = true;
                break;
            }
        }
        //如果是则不用进行任何操作,让它继续将连击动画播放完毕,如果不是,也就是连击动画播放完毕。
        if (!is_Attack)
        {
            attack_combo = 0;//连击数清0
        }

        if (!is_Attack && !is_Move)
        {//如果现在不是在攻击或者移动,则播放站立动画
            animation.Play("Stand");
        }

    }

}
并且指明这里的public GameObject plane就是Plane,则大功告成:

这个程序的关键主要就是这个脚本,主要难点和重点就是:一个,解决自动寻路的状态判定问题,另一个,连击实现的问题。

1、解决自动寻路的状态,虽然判断对象是否到达终点很简单if (navMeshAgent.remainingDistance < 0.01){}甚至if (navMeshAgent.hasPath){}都行,但这个判断并执行,一定要放在鼠标点下左键,让主角寻路之前,反正update()里面的代码是不停被执行的。放在之后有个很严重的问题,在你鼠标点击左键之后,系统会认为主角已经开始跑,但是要跑的路径还未来得及生成,就开始判断是否还有路径没跑完,判断是否离终点有剩余距离,就认为由于路径没生成,就没有寻路路径和离终点没有距离了。所以这个判断一定要放在开始自动寻路之前,否则会直接播放Stand动画,出现主角站着飘B到重点的行为。

2、连击问题,我认为应该这样:用户点击右键,只需要增加连击数,之后再根据连击数播放动画就好,无须像其它人,在点击右键之后的处理动作写一大堆东西。之后,对于连击数的清零,主要还是需要判断 主角的攻击动作是否完成。判断是否处于攻击状态,只能判断它是否处于播放攻击状态了。

这里设置private bool is_Move, is_Attack;//是否在移动、攻击的flag,其主要的意图在最后,if (!is_Attack && !is_Move) //如果现在不是在攻击或者移动,则播放站立动画。如果没有这句,主角完成一系列的动画,不会自动的回复到初始的Stand动画里面的。

这里是Animation连击,也就是指明如何对Unity3D中带有传统动画系统的FBX模型实现连接,所以不能像采用新型动画系统Animator的FBX模型,用AnimatorStateInfo animatorInfo;在配合animatorInfo.normalizedTime > 某百分比,来实现动画是否完成的判断,只能用传统的IsPlaying来搞了。

我认为网上盛传的Animator已经取代Animation完全是扯谈,个人在网上转来一圈,发现收集到的3D模型素材都还是用Animation的。当然如果你是新做的模型,当然要求美工使用Animator,毕竟Animator的方法与类更多,可以更好进行控制。

万万没想到这不足70行代码,居然弄了我一天了,之前玩kof玩combo玩得high翻天,万万没想到实现起来的逻辑居然这么难理解!

2014-09-01 16:29:31 qinyuanpei 阅读数 10006
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

        大家好,欢迎大家关注我的博客,我是秦元培,我的博客地址是blog.csdn.net/qinyuanpei。这段时间博主将大部分的精力都放在了研究官方示例项目上,主要是希望能够从中挖掘出有价值的东西分享给大家,这样博主和大家可以共同学习。好了,那么今天博主想和大家分享的是自动寻路与Mecanim动画系统结合起来实现的一个小案例,希望对大家学习Unity3D能够有所帮助。

       博主曾经告诉大家,博主是一个仙剑迷,平时学习编程累了的时候,博主就会玩玩仙剑,如图是博主偶尔回去玩玩的《新仙剑OL》,仙剑的网游化道路似乎一直不曾平坦,从最初的《仙剑OL》到现在的《新仙剑OL》,仙剑的网游化一直处于不温不火的状态。


          虽然博主比较反感网游,可是作为一款由博主喜欢的单机游戏改编的网游,博主还是忍不住去尝试了一下。基于Unity3D的《新仙剑OL》虽然从画面质量上与同类网页游戏拉开了距离,然而在游戏的玩法和体验上依然没有摆脱国产网游的固定模式,博主叹息之余,便根据这款游戏在自动寻路上的设计写出了这篇文章。说到自动寻路,这几乎是目前所有网页游戏的标准配置了,在博主看来,自动寻路在简化玩家寻找目标的途径的同时,弱化了玩家的参与,使得本该玩家自行探索的游戏世界,变成了枯燥无味的鼠标游戏。曾几何时,我们在迷宫中探索着远方的路,曾几何时,我们开启机关、破解阵法收获着游戏的乐趣。在这样一个快节奏的时代,我们却只能一遍遍地回想着过去的生活,那个时候没有电脑、没有智能手机、没有电影.....可是我们却常常怀念那个时候简单的生活,所以,小时候,幸福是件简单的事情,长大后,简单是件幸福的事情。尽管博主讨厌自动寻路这一设定,可是这与我们学习Unity3D无关,因为我们只是为了更好地使用这个引擎来做出好的游戏产品。好了,闲话少叙,我们继续说《新仙剑OL》中的自动寻路,在游戏中玩家可以通过鼠标来控制人物,如果距离较远,则人物会以奔跑的形式到达目标位置,否则人物将步行到目标位置。而这就是我们今天想要实现的效果。好了,下面我们正式开始今天的内容,今天的内容呢,分为两个部分,第一部分讲述Mecanim动画系统,第二部分讲述自动寻路。

 

第一部分:Mecanim动画系统

         在这一部分,我们主要讲的是动画的切换,因为从刚才的描述中我们知道,角色的动画有三种状态,即Idle、Walk、Run。通过前面的学习呢,我们知道通过Mecanim动画系统的状态机我们可以很方便地实现动画状态的切换。由此我们就可以理出一个大致的思路,通过鼠标点击获取鼠标位置,然后我们利用射线的方法,从摄像机发射一条经过该点的射线,则射线与地面的交点就是我们寻路的目标点,我们通过计算角色与目标点之间的距离来确定角色要采用什么样的动画,而寻路则交给Unity3D的Nav Mesh Agent组件来完成,这就是我们今天的实现思路,其实博主在之前的一篇文章中已经讲述过这种方法,这里不过是在之前的基础上,结合Mecanim动画系统和自动寻路组件做了些改进而已。好了,我们打开我们的项目,如图,是博主创建的一个简单的场景,我们今天的内容就以这个场景来展开:


接下来,我们利用Mecanim动画系统来设计角色的动画,如图,在今天的项目中角色只有三种状态,因此我们可以将三种状态连接起来,这里我们定义了两个Bool类型的变量IsWalk和IsRun,默认为False,这是我们今天用来切换动画的两个变量开关。剩下的工作就是编写脚本来控制动画了,这一步我们放在第二部分来讲。



第二部分:自动寻路

      在这一部分,我们首先要对场景进行烘培,因为Nav Mesh Agent组件是根据网格来计算寻路的路线的,所以烘培的过程相当于是在保存场景中的网格信息,只有这样我们才能够使用Unity3D的寻路组件。下面我们就来讲解下场景的烘焙:

      首先我们选中场景中不需要与玩家发生交互的物体,或者可以认为这些物体是我们的角色需要避开的障碍物,将其设置为Static,如图:


        接下来我们通过Windowe->Navigation命令打开Navigation窗口,此时被选中的物体以深色显示,确认无误后,点击Bake按钮对场景进行烘焙,这样我们就完成了场景的烘焙工作。


       好了,下面我们选中场景中的角色,为其添加Nav Mesh Agent组件,此时场景中会显示出绿色的区域,其含义是这些区域角色可以到达。关于Nav Mesh Agent组件的参数,大家可以自行查阅API文档,这里不做解释了,好了,下面我们编写脚本:

using UnityEngine;
using System.Collections;

public class PeopleScripts : MonoBehaviour {

	//动画组件
	private Animator mAnim;
	//移动速度
	public float MoveSpeed=2.5F;
	//寻路组件
	private NavMeshAgent mAgent;
	//寻路目标标记
	private GameObject Ball;
    
	//寻路标记预制件
	public GameObject PrefabBall;

	void Start () {
	  //获取动画组件
	  mAnim=GetComponent<Animator>();
	  //获取寻路组件
	  mAgent=GetComponent<NavMeshAgent>();
	}

	void Update () {
		//按下鼠标左键
		if(Input.GetMouseButton(0))
		{
			//获取鼠标位置
			Vector3 mPos=Input.mousePosition;
			//利用射线法取得目标位置
			Ray mRay=Camera.main.ScreenPointToRay(mPos);
			RaycastHit mHit;
			if(Physics.Raycast(mRay,out mHit))
			{
				//这里对应于场景中的地面、墙体、楼梯三种结构
				if(mHit.collider.tag=="Ground" || mHit.collider.tag=="Wall" || mHit.collider.tag=="Ti")
				{
				   //获得目标位置
				   Vector3 mTarget=mHit.point;
				   //使用完全面向目标的旋转
				   transform.LookAt(mTarget);
				   //使用平滑转身转向目标
				   //SmoothRotate(mTarget);
				   //计算距离
				   float mDistance=Vector3.Distance(mTarget,this.transform.position);
				   //当距离大于4时奔跑到目标位置,否则步行到目标位置
				  if(mDistance>4F){
					  mAnim.SetBool("IsRun",true);
				  }else{
					  mAnim.SetBool("IsWalk",true);
				  }
				  
				  //根据不同的结构生成不同高度的寻路目标标记
				  if(mHit.collider.tag=="Ground"){
				     //标记寻路目标
				     Ball=(GameObject)Instantiate(PrefabBall,new Vector3(mTarget.x,0.5F,mTarget.z),
						  Quaternion.identity);
				  }else{
						//标记寻路目标
						Ball=(GameObject)Instantiate(PrefabBall,new Vector3(mTarget.x,3.0F,mTarget.z),
						     Quaternion.identity);
				  }
				  //设置寻路目标
				  mAgent.SetDestination(mTarget);
				}
			}
		}
	}	

	void OnTriggerEnter(Collider mCollider)
	{
		if(mCollider.tag=="Ball")
		{
			//获取目标标记
			GameObject mBall=mCollider.gameObject;
			//销毁目标标记
			Destroy(mBall);
			//将角色状态设为Idle
			mAnim.SetBool("IsRun",false);
			mAnim.SetBool("IsWalk",false);
		}
	}


	//平滑转身,参考自Stealth
	void SmoothRotate(Vector3 target)
	{
		//构造目标朝向
		Quaternion targetRotation = Quaternion.LookRotation(target, Vector3.up);
		//对目标朝向进行插值
		Quaternion mRotation = Quaternion.Lerp(transform.rotation, targetRotation, 15F * Time.deltaTime);
		//赋值
		transform.rotation=mRotation;
	}
}

最后,我们来看看最后的效果吧,为了让大家更清楚的看到寻路的效果,博主在寻路点处增加了一个紫色的小球,便于大家观察:



每日箴言:别低头,别诉苦。他们以为你无坚不摧,那就随他们喜欢;别人不知道的,你没必要反驳给他听。




喜欢我的博客请记住我的名字:秦元培,我博客地址是blog.csdn.net/qinyuanpei。
转载请注明出处,本文作者:秦元培,本文出处:http://blog.csdn.net/qinyuanpei/article/details/38981669


2019-03-08 16:56:52 qq_44238513 阅读数 989
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

NavMesh(导航网格)是3D游戏世界中用于实现动态物体自动寻路的一种技术,将游戏中复杂的结构组织关系简化为带有一定信息的网格,在这些网格的基础上通过一系列的计算来实现自动寻路。。导航时,只需要给导航物体挂载导航组建,导航物体便会自行根据目标点来寻找最直接的路线,并沿着该线路到达目标点。

建造一个简单的场景

在这里插入图片描述

选中两个球之外所有的物体,然后在Inspector——Static——Navigation Static

在这里插入图片描述

选中要寻路的物体添加在导航栏选中Component——Navigation——Nav Mesh Agent组件

在这里插入图片描述

在导航栏打开Window——Navigation窗口

在这里插入图片描述

在Navigation窗口里面选择Bake然后再选择右下角的Bake

在这里插入图片描述

上面所有的设置好后上代码,把这个代码组件放到寻路的物体上

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class AI : MonoBehaviour {

    public GameObject target;
    private NavMeshAgent navMeshAgent;

	void Start ()
    {
        navMeshAgent = GetComponent<NavMeshAgent>();
        if(navMeshAgent==null)
        {
            navMeshAgent = gameObject.AddComponent<NavMeshAgent>();
        }
	}
	

	void Update ()
    {
            navMeshAgent.SetDestination(target.transform.position);
	}
}

然后把目标拖到代码组件的Target里面

在这里插入图片描述

运行就OK了

2013-10-12 16:19:50 woailvmengmeng 阅读数 0
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

众所周知,自动寻路是所有游戏的一个难点,属于AI(人工智能)的范畴。一个游戏的AI的设计是否足够完美,可能决定了这个游戏的命运。然而自动寻路就是AI中的一个十分重要的分支,其算法异常复杂。然而unity3d中提供了一套非常成熟的组件来为我们解决这一难题。今天,我们就来一起欣赏一下Unity3d自带的自动寻路系统。

        我们在学习一个陌生的知识时,免不了要记一些令人烦恼的概念,自动寻路也是一样的。但是记东西也是有技巧的,我认为我们应该先看到一点惊喜再学东西,这样兴趣有了,学东西就不那么抵触了。所以我现在先列举举一个简单的例子,记住,先不要思考为什么我要这样做,如果你是初次学习这个自动寻路的话最好这样,不然你会走很多弯路的。我们需要新建一个工程,将其起名为:NavMeshProject。然后我们可以制作场景了。
        像往常一样,先做一个地面,这里我用的是Cube。然后我将我们的朋友Robot也请来了,看这:



是不是很帅啊!我特意加了个灯光,给地面换了个柔和的颜色,看起来就不那么单调了。然后我们保存一下整个场景,该场景取名为:TestNavgation1。
下面我来烘焙场景,但是记住,先不要想为什么。Unity3d编辑器的菜单下:Window->Navigation,这是我们可以发现编辑器的某一部分出现了一个Navigation窗口,如:


请注意一下此图的右下角的一个Bake按钮。
在Hierarchy下选中Plane(就是那个地面),然后我们在Navigation面板中的Object选项卡下找到Navigation Static复选框,勾选它,然后点击Navigation面板右下角的Bake按钮:


此时我们可以发现Project面板下面多出了一个文件夹,且此文件夹出现了一个子文件NavMesh: 



并且Scene窗口中的地面上出现了一下变化: 



然后我们跟robot添加一个组件:NavMeshAgent。具体做法是 :  在Hierarchy面板下选中robot,然后在Unity3d菜单下:Component->Navigation->NavMeshAgent,你会发现robot身上出现了一个类似胶囊体碰撞器的绿色线框的包围体,调节一下盖NavMeshAgent组件中的一些参数:Height,BaseOffset等,如图: 



最后我们该编写脚本了。我还是新建一个文件夹:Scripts。然后编写两个脚本:
脚本一,专门设置导航网格代理的目的地的:

using UnityEngine;
using System.Collections;

public class NavMeshMove : MonoBehaviour {

    public Transform[] NavMeshTransforms;//导航网格的目的地组。

    private NavMeshAgent nma;//Robot的导航网格代理

void Start () {
        if(NavMeshTransforms == null)
        {
            return;
        }
    nma = gameObject.GetComponent<NavMeshAgent>();
            nma.SetDestination(NavMeshTransforms[0].position);//初始时刻设置的导航网格代理的目的地
}

void Update () {

        if(nma.remainingDistance == 0){
            //当导航网格代理到达了目的地时,更换目的地,且是随机的更换
            nma.SetDestination(NavMeshTransforms[Random.Range(0,NavMeshTransforms.Length)].position);
        }
   }
}

脚本二,是专门为导航网格代理编写的动画控制脚本:

using UnityEngine;
using System.Collections;

public class NavNeshAnimation : MonoBehaviour {

    public float***nAnimaitonSpeed = 4.0f;//定义最大的跑步速度,一般为导航网格代理的速度
    public float speedThreshold = 0.1f;//定义动画从idle***n过渡的临界速度

    private string loadAnimation = "load_Idle";//定义需要执行的协同函数名
    private NavMeshAgent nma;//定义导航网格代理

IEnumerator Start () {
    nma = gameObject.GetComponent<NavMeshAgent>();
        AnimationSetup();//简单的设定一下动画。

        while(Application.isPlaying) {
            yield return StartCoroutine(loadAnimation);//执行协同函数
    }
    }

    IEnumerator load_Idle() 
    {
        do{
            UpdateAnimationBlend();
            yield return null;
            
        }while(nma.remainingDistance == 0);

        loadAnimation = "load_Run";
        yield return null;
    }

    IEnumerator load_Run()
    {
        do
        {
            UpdateAnimationBlend();
            yield return null;

        } while (nma.remainingDistance != 0);

        loadAnimation = "load_Idle";
        yield return null;

    }

    void AnimationSetup() 
    {
        animation["idle"].layer = 1 ;
        animation["***n"].layer = 1;
        animation.SyncLayer(1);

        animation.CrossFade("idle", 0.1f, PlayMode.StopAll);
    }
    
    void UpdateAnimationBlend() 
    {
        Vector3 velocityXZ = new Vector3(nma.velocity.x,0.0f,nma.velocity.z);
        float speed = velocityXZ.magnitude;
        animation["***n"].speed = speed /***nAnimaitonSpeed;

        if (speed > speedThreshold)
        {
            animation.CrossFade("***n");
        }
        else {
            animation.CrossFade("idle");
        }
    
    }
}

我们将这两个脚本绑定到robot身上,然后我们在Hierarchy面板中新建一个空的GameObject,重命名为:NavMeshPoint,并且新建5个spere作为它的子对象被其管理,如下:




然后将这5个spere分开到Plane上的不同位置,如下: 



选中robot,然后在Inspector下的NavMeshMove脚本上设置NavMeshTransforms的个数为5,并且将那5个spere分别拖拽到相应的位置,如下: 



好了,我们可以运行一下工程,看一下效果,这里只截了部分图片: 









从图中我们可以看出,我们的robot在不断的跑步,每次跑到目标点之后停下来,然后转身朝向下一个目标点,如果工程没有停止,那么它周而复始的循环下去,这对于编写NPC的AI很有帮助。
是不是很神奇呢?有没有想要将这部分知识融会贯通的冲动?我把工程一起献上吧。这个星期我的连续几篇文章,会深入的介绍这一系列的知识。敬请期待,不过我建议一下读者实现最好了解一些这个组件,看看与我的理解有什么不同,我们一同探讨,共同切磋。