怪物的AI脚本(2)

3 篇文章 0 订阅
3 篇文章 0 订阅

1. C#字典集合HashTable,Dicrionary,ConcurrentDictionary三者区别

https://blog.csdn.net/yinghuolsx/article/details/72952857 作者:yinghuolsx

C#中 HashTable, Dictionary, ConcurrentDictionar 三者都表示键/值对的集合。

1.HashTable
HashTable表示键/值对的集合。
System.Collections;
Key通常用来快速查找,同时Key区分大小写;value用来储存对应于key的值。
HashTable中的key-value值均对应于object类型,所以HashTable可以支持对应任何类型的key-value键值对,任何非null的对象都可以用作键或者值。

当一个HashTable被占用一大半的时候我们通过计算散列值取得的地址值可能会重复指向同一地址,这就造成哈希冲突。

using System;
using System.Collections;

namespace WebApp
{
    class Program
    {
        static void Main(string[] args)
        {   
            Hashtable myHash=new Hashtable();
            
            //插入
            myHash.Add("1","joye.net");
            myHash.Add("2", "joye.net2");
            myHash.Add("3", "joye.net3");

            //key 存在
            try
            {
                myHash.Add("1", "1joye.net");
            }
            catch
            {
                Console.WriteLine("Key = \"1\" already exists.");
            }
            //取值
            Console.WriteLine("key = \"2\", value = {0}.", myHash["2"]);

            //修改
            myHash["2"] = "http://www.cnblogs.com/yinrq/";
            myHash["4"] = "joye.net4";   //修改的key不存在则新增
            Console.WriteLine("key = \"2\", value = {0}.", myHash["2"]);
            Console.WriteLine("key = \"4\", value = {0}.", myHash["4"]);

            //判断key是否存在
            if (!myHash.ContainsKey("5"))
            {
                myHash.Add("5", "joye.net5");
                Console.WriteLine("key = \"5\": {0}", myHash["5"]);
            }
             //移除
            myHash.Remove("1");

            if (!myHash.ContainsKey("1"))
            {
                Console.WriteLine("Key \"1\" is not found.");
            }
            //foreach 取值
            foreach (DictionaryEntry item in myHash)
            {
                Console.WriteLine("Key = {0}, Value = {1}", item.Key, item.Value);
            }
            //所有的值
            foreach (var item in myHash.Values)
            {
                Console.WriteLine("Value = {0}",item);
            }

            //所有的key
            foreach (var item in myHash.Keys)
            {
                Console.WriteLine("Key = {0}", item);
            }
            Console.ReadKey();
        }
    }
}

在这里插入图片描述
2.Dictionary
Dictionary<TKey,TValue>泛型类提供了从一组键到一组值的映射。通过键来检索值的速度是非常快的。因为它是作为一个Hash表来实现的,是一种变种的HashTable,采用一种分离链接散列表的数据结构来解决Hash冲突的问题。

using System;
using System.Collections;
using System.Collections.Generic;

namespace WebApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<string, string> myDic = new Dictionary<string, string>();
            
            //插入
            myDic.Add("1", "joye.net");
            myDic.Add("2", "joye.net2");
            myDic.Add("3", "joye.net3");

            //key 存在
            try
            {
                myDic.Add("1", "1joye.net");
            }
            catch
            {
                Console.WriteLine("Key = \"1\" already exists.");
            }
            //取值
            Console.WriteLine("key = \"2\", value = {0}.", myDic["2"]);

            //修改
            myDic["2"] = "http://www.cnblogs.com/yinrq/";
            myDic["4"] = "joye.net4";   //修改的key不存在则新增
            Console.WriteLine("key = \"2\", value = {0}.", myDic["2"]);
            Console.WriteLine("key = \"4\", value = {0}.", myDic["4"]);

            //判断key是否存在
            if (!myDic.ContainsKey("5"))
            {
                myDic.Add("5", "joye.net5");
                Console.WriteLine("key = \"5\": {0}", myDic["5"]);
            }
             //移除
            myDic.Remove("1");

            if (!myDic.ContainsKey("1"))
            {
                Console.WriteLine("Key \"1\" is not found.");
            }
            //foreach 取值
            foreach (var item in myDic)
            {
                Console.WriteLine("Key = {0}, Value = {1}", item.Key, item.Value);
            }
            //所有的值
            foreach (var item in myDic.Values)
            {
                Console.WriteLine("Value = {0}",item);
            }

            //所有的key
            foreach (var item in myDic.Keys)
            {
                Console.WriteLine("Key = {0}", item);
            }
            Console.ReadKey();
        }
    }
}

在这里插入图片描述
3.ConcurrentDictionary
表示可由多个线程同时访问的键/值对的线程安全集合。
System.Collections.Concurrent;

System.Collections.Concurrent 命名空间提供多个线程安全集合。当有多个线程并发访问集合时,应该使用这些类代替System.Collections和System.Collections.Generic命名空间中的对应类型。

Summary:
大数据插入Dictionary花费时间最少

遍历HashTable最快是Dictionary的1/5,ConcurrentDictionary的1/10

单线程建议用Dictionary,多线程建议用ConcurrentDictionary或者HashTable(Hashtable tab = Hashtable.Synchronized(new Hashtable());获得线程安全的对象)

2.C#迭代器

https://blog.csdn.net/u013477973/article/details/65635737 作者:O213

foreach做遍历确实很方便,但是并不是每一种类型都能使用foreach进行遍历操作,只有实现了IEnumerable接口的类(也叫做可枚举类型)才能进行foreach的遍历操作,集合和数组已经实现了这个接口,所以才能进行foreach的遍历操作!

IEnumerable & IEnumerator
IEnumerable叫做可枚举接口,它的成员只有一个
GetEnumerator()
返回一个枚举器对象,即实现了IEnumerator接口的实例,实现IEnumerator接口的枚举器包含3个函数成员:
Current属性,MoveNext()方法,Reset()方法。
Current属性为只读属性,返回枚举序列中的当前位置,
MoveNext()把枚举器的位置前进到下一项,返回布尔值,新的位置若是有效的,返回true,否则返回false,
Reset()将位置重置为原始状态。

class Enumerator1<T> : IEnumerator<T> {
private int _position = -1;
private T[] t;
public Enumerator1(T[] a) {
    t = new T[a.Length];
    for (int i = 0; i < t.Length; i++) {
        t[i] = a[i];
    }
}    
public T Current
{
    get
    {
        if (_position == -1) {
            throw new InvalidOperationException();
        }
        return t[_position];
    }
}
object IEnumerator.Current
{
    get
    {
        if (_position == -1)
        {
            throw new InvalidOperationException();
        }
        return t[_position];
    }
}
public void Dispose()
{         
}
   public bool MoveNext()
{
    Console.WriteLine("Call Move Next");
    if (_position >= t.Length)
        return false;
    else
    {
        _position++;
        return _position < t.Length;
    }           
}
public void Reset()
{
    _position = -1;
}

}

…后面都看不懂

迭代器和yield return
当实现了可枚举类型,在foreach中完成遍历时,就需要手动实现IEnumerable枚举接口和IEnumerator枚举器接口。
C#2.0以后提供了更为简单的方法取创建可枚举类型和枚举器,那就是迭代器,迭代器会生成枚举器和枚举器类型。

 public  IEnumerator<string> Color() //迭代器
    {
        yield return "red";
        yield return "green";
        yield return "blue";
    }   

以上就是一个迭代器创建枚举器的过程,如果手动创建枚举器,是需要实现IEnumerator接口的,而在这里,并没有实现Current,MoveNext(),Rest这些成员,
取而代之的是使用yield return语句,简单来说迭代器就是使用一个或多个yield return语句告诉编译器创建枚举器类,yield return语句指定了枚举器中下一个可枚举项:

 class Program{   
    static void Main()
    {
        ColorEnumerable color = new ColorEnumerable();
        foreach (var item in color)
        {
            Console.WriteLine(item);
        }
        Console.ReadKey();
    }}  
class ColorEnumerable {
    public IEnumerator<string> GetEnumerator()
    {
        return Color();
    }
    public IEnumerator<string> Color()
    {
        yield return "red";
        yield return "green";
        yield return "blue";
    }
}   

ColorNumerable类实现了GetEnumerator()方法,为可枚举类型。迭代器可以返回一个枚举器类型,也可以返回一个可枚举类型。

public IEnumerable<string> Color() {
        yield return "red2";
        yield return "green2";
        yield return "blue2";
    }  

e.g:

class Program{   
    static void Main()
    {
        ColorEnumerable2 color2 = new ColorEnumerable2();
        foreach (var item in color2.Color())
        {
            Console.WriteLine(item);
        }
        Console.ReadKey();
    }} 
  class ColorEnumerable2 {
    public IEnumerable<string> Color() {
        yield return "red2";
        yield return "green2";
        yield return "blue2";
    }
}   

利用迭代器返回一个IEnumerable的可枚举类型:

class Program{   
    static void Main()
    {
        ColorEnumerable2 color2 = new ColorEnumerable2();
        foreach (var item in color2.Color())
        {
            Console.WriteLine(item);
        }
        Console.ReadKey();
    }} 
  class ColorEnumerable2 {
    public IEnumerable<string> Color() {
        yield return "red2";
        yield return "green2";
        yield return "blue2";
    }
}   

在foreach的语句中用的是color2.Color(),而不是像上一个例子中直接使用的color,这是因为ColorEnumerable类中已经公开实现了GetEnumerator()方法,但在ColorEnumerable2类中并没有公开实现,所以不能使用foreach直接遍历ColorEnumerable2类的实例,但是ColorEnumerable2的方法Color()使用迭代器创建了可枚举类,已经实现了GetEnumerator()方法,所以使用color2.Color()可以得到可枚举类在foreach中进行遍历。

可以做如下修改:

class ColorEnumerable2 {
    public IEnumerator<string> GetEnumerator() {
        return Color().GetEnumerator();
    }
    public IEnumerable<string> Color() {
        yield return "red2";
        yield return "green2";
        yield return "blue2";
    }
}   

这样就可以在foreach的语句中直接使用ColorEnumerable2的实例进行遍历了。

记住在枚举器类 (Enumerator) 中是

public IEnumerator<string> GetEnumerator()
{
   return Color();
}

而在枚举类 (Enumerable) 中是:

public IEnumerator<string> GetEnumerator()
{
    return Color().GetEnumerator();
}

总结:
yield return 可以根据返回类型告诉编辑器创建可枚举类或者是枚举器
yield return 语句指定了枚举器对象中的下一个可枚举项

3. unity中的StartCoroutine和yield return

unity协程的定义:
A coroutine is a function that is executed partially and, persuming suitable conditions are met, will be resumed at some point in the future until its work is done
即协程是一个分部执行,遇到条件(yield return)会挂起,直到满足条件才会被唤醒继续执行后面的代码。

bool isStartCall = false;
bool isUpdateCall = false;
bool isLateUpdateCall = false;
void Start () {
    if (!isStartCall) {
        Debug.Log("Start Call Begin");
        StartCoroutine("StartCoroutines");
        Debug.Log("Start Call End");
        isStartCall = true;
    }
}
IEnumerator StartCoroutines() {
    Debug.Log("StartCoroutine Call Begin");
    yield return null;
    Debug.Log("StartCoroutine Call End");
}
void Update() {
    if (!isUpdateCall) {
        Debug.Log("Update Call Begin");
        StartCoroutine("UpdateCoroutines");
        Debug.Log("Update Call End");
        isUpdateCall = true;
    }     
}
IEnumerator UpdateCoroutines()
{
    Debug.Log("UpdateCoroutine Call Begin");
    yield return null;
    Debug.Log("UpdateCoroutine Call End");
}
void LateUpdate() {
    if (!isLateUpdateCall) {
        Debug.Log("LateUpdate Call Begin");
        StartCoroutine("LateUpdateCoroutines");
        Debug.Log("LateUpdate Call End");
        isLateUpdateCall = true;
    }
}
IEnumerator LateUpdateCoroutines()
{
    Debug.Log("LateUpdateCoroutine Call Begin");
    yield return null;
    Debug.Log("LateUpdateCoroutine Call End");
}  

这代码不是很懂…
https://dsqiu.iteye.com/blog/2029701 作者:DSQiu

4. 使用枚举来建立物品系统

using System.Collections;
using System.Collections.Generic; //Dictionary included
using UnityEngine;


public class Widget_Inventory : MonoBehaviour {

    public enum InventoryItem //define a enum to distinguish the items 
    {
        ENERGYPACK, //for adding the energy
        REPAIRKIT,  //for repairing the health

    }

    public Widget_Status playerStatus;
    private float repairKitHealAmt = 5.0f;
    private float energyPackHealAmt = 5.0f;

    Dictionary<InventoryItem, int> widgetDict;

	// Use this for initialization
	void Start () { 
        playerStatus = GetComponent<Widget_Status>(); //Initialization
        widgetDict = new Dictionary<InventoryItem, int>(); //Initialization!!!important!!!

        widgetDict.Add(InventoryItem.ENERGYPACK, 1); //Initialization!!!
        widgetDict.Add(InventoryItem.REPAIRKIT, 2);  //Initialization!!!
	}
	
	public void GetItem(InventoryItem item, int amount)
    {
        widgetDict[item] += amount; //widgetDict[item]就是item对应着的那个值
        print(widgetDict[item]);
    }

    public void UseItem(InventoryItem item, int amount)
    {
        if (widgetDict[item] <= 0)
        {
            return;
        }
        else
        {
            widgetDict[item] -= amount;
        }

        switch (item)  //Use functions depending on different situations!
        {
            case InventoryItem.ENERGYPACK:
                playerStatus.AddEnergy(energyPackHealAmt);
                break;
            case InventoryItem.REPAIRKIT:
                playerStatus.AddHealth(repairKitHealAmt);
                break;          
        }
    }

    public bool CompareItemCount(InventoryItem compItem, int compNumber)
    {
        return widgetDict[compItem] >= compNumber;
    }

    public int GetItemCount(InventoryItem compItem)
    {
        return widgetDict[compItem];
    }
}

5.怪物的AI

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

public class EBunny_AIController : MonoBehaviour {

    public Transform target;
    public Animation animation;   
    public float directionTraveltime = 2.0f; //怪物转方向的时间间隔
    public float rotateSpeed = 30.0f;
    public float idleTime = 1.5f;
    public float walkSpeed = 3.0f;
    public float attackDistance = 15.0f; //怪物巡逻范围 
    public float attackRadius = 5.0f; //怪物攻击的范围
    public float attackMoveSpeed = 2.5f; //怪物确认攻击后的移动速度
    public bool isAttacking = false;
    public Vector3 attackPosition = new Vector3(0, 1, 0);
    public float damage = 1.0f; //怪物的伤害值
    

    private CharacterController characterController;
    private float timeToNewDirection = 0.0f; //计时器
    private Vector3 distanceToPlayer; //怪物攻击距离
    private float lastAttackTime = 0.0f;

    // Use this for initialization
    void Start () {
        characterController = GetComponent<CharacterController>();
        animation = GetComponent<Animation>();
        if (!target)
        {
            target = GameObject.FindWithTag("Player").transform;  //because we define "Player" as a Transform component
        }
        //animation
        animation.wrapMode = WrapMode.Loop;
        animation["EBunny_Death"].wrapMode = WrapMode.Once;
        //设置三个动画之间的层级关系,谁在最上面,谁最优先...
        animation["EBunny_Death"].layer = 5;
        animation["EBunny_Hit"].layer = 3;
        animation["EBunny_Attack"].layer = 1;

        StartCoroutine(InitEnemy());

	}

    IEnumerator InitEnemy()
    {
        while (true) //因为条件是true,所以该循环是一直在循环的(添加条件使得其可以跳出)
        {
            //Non-stop switching between the two states below
            yield return StartCoroutine(Idle());  //启动协程
            yield return StartCoroutine(Attack());
        }
    }
    
    IEnumerator Idle()
    {
        //Change its direction after the setting time
        while (true)
        {
            if (Time.time > timeToNewDirection)  //系统时间大于计时器时间,意味着要开始转方向
            {
                //转方向的时候有一定的停顿,然后再转过去...像是在思考...
                yield return new WaitForSeconds(idleTime);
                 
                //通过随机函数来进行转向,并设定转向的速度
                if (Random.value> 0.5) //random number
                {
                    transform.Rotate(new Vector3(0, 5, 0), rotateSpeed);
                }
                else
                {
                    transform.Rotate(new Vector3(0, -5, 0), rotateSpeed);
                }
                //同时计时器的时间也要更新
                timeToNewDirection = Time.time + directionTraveltime;
                
            }
            //开始往前跑(把自身坐标转换到世界坐标)
            Vector3 walkForward = transform.TransformDirection(Vector3.forward);
            characterController.SimpleMove(walkForward * walkSpeed);

            //进入主角一定范围内则进行判断
            distanceToPlayer = transform.position - target.position; //算出与目标之间的距离 
            if (distanceToPlayer.magnitude < attackDistance) //进行判断,注意向量的长度要用到magnitude
                                                             //如果两者之间的距离小于巡逻距离,意味着主角已经进入怪物攻击的范围
            {
                yield break; //跳出循环
            }
            yield return null;
        }
    }

    IEnumerator Attack()
    {
        isAttacking = true;
        //播放攻击动画??
        animation.Play("EBunny_Attack");

        //怪物要开始盯着主角
        transform.LookAt(target);
        //跑向主角
        Vector3 direction = transform.TransformDirection(Vector3.forward * attackMoveSpeed);
        characterController.SimpleMove(direction);

        //进行判断,如果进入攻击范围内就要发动攻击
        bool lostSight = false; //怪物是否在攻击范围内
        while (!lostSight)
        {
            //Learn transform.TransformPoint!!!
            Vector3 location = transform.TransformPoint(attackPosition) - target.position;//计算与玩家的距离
            //添加一个计时器,让怪物每次攻击之间有微小的间隙,不能一直不断攻击
            if (Time.time > lastAttackTime + 2.0f && location.magnitude < attackRadius) //满足条件则发动攻击
            {
                //注意SendMassage方法!!!——SendMassage朝本级别物体的多个脚本发送消息(target上面的所有脚本都收到消息,
                //如果有ApplyDamage这个方法,那么就会调用
                target.SendMessage("ApplyDamage", damage);
                lastAttackTime = Time.time;
            }
            if (location.magnitude > attackRadius)
            {
                lostSight = true; //证明怪物已经丢失了target,可以退出循环了
                yield break;
            }
            yield return null;
        }

        isAttacking = false;
    }
	
	// Update is called once per frame
	void Update () {
		
	}
}

6.怪物的状态

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

public class EBunny_Status : MonoBehaviour {

    public float health = 10.0f;

    private bool isDead = false;

	// Use this for initialization
	void Start () {
        
    }
	
	// Update is called once per frame
	void Update () {
		
	}

    public void ApplyDamage(float damage)
    {
        if (health <= 0)
        {
            return;
        }
        health -= damage;
  
        GetComponent<Animation>().Play("EBunny_Hit");
        if (health <= 0 && !isDead)
        {
            //?????
            health = 0;
            isDead = true;
            StartCoroutine(Die());
        }
    }

    IEnumerator Die()
    {

        GetComponent<Animation>().Stop(); //停止播放当前正在播放的动画,防止动画串了
        GetComponent<Animation>().Play("EBunny_Death");
        Destroy(this.GetComponent<EBunny_AIController>());
        yield return new WaitForSeconds(2.0f);
        Destroy(this.gameObject);
    }

    public bool IsDead()
    {
        return isDead;
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值