2013-11-14 09:49:06 chenluwolf 阅读数 1307
  • Unity 值得看的500+ 技术内容列表

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

Unity3D中随机函数的应用

电子游戏中玩家与系统进行互动的乐趣绝大多数取决于事件发生的不可预知性和随机性。在unity3DAPI中提供了Random类来解决随机问题。

最简单的应用就是在数组中随机选择一个元素,使用Random内置的方法可以进行选择。代码如下:

Floatelement=myArray[Random.Range(0,myArray.Length)];

Random.Range方法的两个参数代表了选择数据元素的范围。

很多时候,游戏中运用更复杂的随机事件,下面描述几种常见的随机事件案例

1:随机选择具有不同概率的事件

当你你需要选择具有不同发生概率的事件元素时。运用的场景为在游戏中玩家遇到NPC时,NPC在系统内部有好几种与玩家交互的方法。但是这些方法发生的概率并不相等。例如有50%几率进行问好;25%几率跑开;20%几率攻击玩家;5%几率为玩家提供礼品。

学过概率统计学的同学,一定做过在连续时间内发生事件概率的模型。我们也可以将上面描述的需求抽象为一个模型,我们将整个NPC与玩家交互系统看做一个纸带,按照事件发生的概率将纸带分为若干段,将大概率事件排在前面,小概率事件排在后面。当玩家遇到NPC时代表在这个纸带上随机选择一个点。抽象结果用图如下:

随机选择的点在哪个事件范围内就会发生相应的事件。将这个模型用代码表示为:

复制代码
float Choose(float[] Probs)
{
//将事件元素加入到数组中,如上面有4个元素,分别为50,25,20,5
    {
        float total=0;
        Foreach(float elem in probs )
        Total+=elem;
}
//Random.value方法返回一个0—1的随机数
float randomPoint=Random.value*total;
for(int i=0;i<pros.Length;i++ )
{
    if(randomPoint<probs[i])
    return i;
    else
    randomPoint-=probs[i];
}
return probs.length-1;
    }
复制代码

过程描述:首先检查随机数是否小于第一个元素,如果小于那么第一个元素就被选择了。否则用随机数的值减去第一个元素的值然后与第二个元素进行比较,以此类推直到找到正确的元素为止。

过程验证:如上图所示,假如随机数最终为82,那么首先将82与第一个元素50比较:82>50,因此应该将82-50=32与第二个元素25进行比较:32>25,因此应该将32-25=7与第三个元素20进行比较:7<20,那么第三个元素被选中,相对应的事件发生。

2:使用随机数打乱数组顺序

这个案例应用的最好场景就是纸牌游戏中的洗牌操作,思想也比较简单,就是依次将数组内的每个元素与一个随机序号的元素进行交换。代码如下:

复制代码
void Shuffle(int[] cards)
{
    for(i=0;i<cards.Length;i++)
    {
        int temp=cards[i];
        int randomIndex=Random.Range(0,cards.Length);
        cards[i]=cards[randomIndex];
        cards[randomIndex]=temp;
        }
}
复制代码

3:使用随机数在数组中无重复的选择元素

在概率论中我们常常见到这样题目:一个球队10个人,要选择5个人去参加比赛。在游戏中的应用场景为你希望在几个不同的点随机生成一个不相同的NPC。例如需要在10个NPC中随机选择5个,第一个NPC被选中的概率为5/10,如果第一个被选中了那么第二个被选中的概率就变成了4/9,如果第一个被淘汰那么第二个被选中的概率就为5/9

复制代码
Transform[] spawnPoints;
Transform[] ChooseSet(int numRequired)
{
    Transform [] result=new Transform[numRequired];
    Int numToChoose=numRequired;
    for(int numLeft=spawnPoints.Length;numLeft>0;numLeft--)
    {
        float prob=(numToChoose+0.0)/(numLeft+0.0);
        if(Random.value<=prob)
        {
            numToChoose--;
            result[numToChoose]=spawnPoints[numLeft-1];
            if(numToChoose==0)
            break;
    }
}
return result;
}
        
复制代码

过程描述:扫描待选择的NPC数组,如果生成的随机数小于被选中的概率则表示该元素被选中,直到所需要的元素个数都被选中。

注意点:进行选择的时候应该先将待选择的NPC数组进行“洗牌“操作。

2013-11-30 11:02:26 luyuncsd123 阅读数 7113
  • Unity 值得看的500+ 技术内容列表

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

  声明:   本博客文章原创类别的均为个人原创,版权所有。转载请注明出处: http://blog.csdn.net/ml3947,另外本人的个人博客:http://www.wjfxgame.com


  对于AStarPathFinding,很多人应该不会陌生。这个算是Unity3D里最好用的寻径插件了。不论是3D游戏,还是2D游戏,我们都可以使用它来进行A*算法的寻径。之前翻译的“使用免费工具进行2D游戏开发”的教程中本来应该有一节是AStarPathFinding寻径的,但是在这个教程翻译了三节之后,发现后面其实作者只是单纯把脚本什么的都给出下载地址,并未做介绍,而对Orthello的介绍在前面几节就结束了,所以就没有继续翻译。这里另外开一篇文章来单独介绍寻径。


  下面我们来看看,应该如何使用AStarPathFinding


  首先制作一个游戏场景。

  

  大家可以看到,这个是使用了一个Terrain,然后放置了几个房子和灯光以及树木等。

  

  我们创建一个空的Object,然后通过Component->Pathfinding->PathFinder添加脚本。这个是AStarPathFinder里最重要的脚本。将它改名为AStar。


  


  我们可以从右边看到有很多的栏目。

  其实寻径的原理,还是以网格的形式分布到地面,排除掉一些障碍物的相应的层,亦或者进行高度测试使得一定的高度不可行走。然后使用AStar寻径算法来寻找最短的路径。

  首先创建一个Grid Graph


  

  然后在这边调整网格的数目和大小。

  对于我们的3D游戏而言,网格的大小可以就设置为1( 2d游戏通常根据地图单元格大小来设置)。然后设置WidthDepth调整整个网格的大小。记住高度一定要与地形高度相同。

  最后点最下面的Scan。会搜索出所有可行走的路径,然后以某种颜色的形式显示在网格上。


  这里有几个很重要的参数。

  首先是Max ClimbClimb Axis。这个是指的最大攀爬的高度以及攀爬的轴的方向。通常而言,这个轴一定要与地形垂直,不然地面所有地方将无法通过。


  然后是Collision Test。这里是在寻径中对碰撞的一些检测。

  

  

  例如上图,我们对Default层进行了碰撞的检测,那么在寻径的时候,路径将不会通过这个帐篷的周边。


  


  我们看看内部,内部出了中间放置了一盘食物的位置为寻径无法通过的,其他地方都是可以通过的。


  


  当我们把Collision Test取消选中,然后重新Scan,那么在寻径中,整个帐篷的范围将都列入可行走的范围。


  这里要记住寻径上网格的可移动的范围,并不代表你的角色可以从这些范围行走。它只代表的,寻径算法将把这些范围列入为可通过的。然后,如果你的角色跟帐篷有碰撞,那么即使AStar计算出的路径要穿做帐篷,你的角色也是无法做到的。


  当AStar的PathFinder找到路径是,会在Scene中显示一条绿线。

  

  这条绿线就是寻找到的路径。


  


  同样的,通过限制图层碰撞等,我们也可以在2D游戏中使用AStarPathFinding。如上图,虽然绿线不是很清晰,但我们依然可以看到怪物走到我们角色的路径。


  下面我们来看一下,找到路径之后,怎么对路径进行处理。这里是上面2D游戏里的一些简单脚本。


  

[csharp] view plaincopy
  1. public void Start ()  
  2. {  
  3.     controller = GetComponent<Controller> ();  
  4.     seeker = GetComponent<Seeker> ();  
  5.     seeker.pathCallback += searchComplete;  
  6.       
  7.     StartCoroutine(RepeatTrySearch());  
  8. }  
  9.   
  10.   
  11. IEnumerator RepeatTrySearch(){  
  12.     while(true){  
  13.       TrySearchPath();  
  14.       yield return new WaitForSeconds(searchRate);  
  15.     }  
  16. }  
  17.   
  18. IEnumerator WaitForRepath(){  
  19.     if (waitingForRepath) yield break;  
  20.      waitingForRepath = true;  
  21.      yield return new WaitForSeconds(searchRate - (Time.time-lastRepath));  
  22.      waitingForRepath = false;  
  23.      TrySearchPath();  
  24. }  
  25.   
  26. public void TrySearchPath(){  
  27.     if(Time.time - lastRepath >= searchRate && isCanSearch && isCanSearchAgain){  
  28.         lastRepath = Time.time;  
  29.         seeker.StartPath (transform.position, target.position);   
  30.         isCanSearchAgain = false;  
  31.     }else {  
  32.         StartCoroutine(WaitForRepath());      
  33.     }  
  34. }  
  35.   
  36.   
  37.   
  38. public void searchComplete (Path p)  
  39. {  
  40.     path = p;  
  41.     nowPosition = 0;  
  42.     if (path != null) {  
  43.         vPath = path.vectorPath;      
  44.         nextNode = vPath [nowPosition];  
  45.         Debug.Log ("Find Path:" + vPath.Count + "," + vPath [0]);  
  46.     } else {  
  47.         Debug.Log ("Find No Path");   
  48.     }  
  49.     isCanSearchAgain = true;  
  50.     isMoveFinished = false;  
  51. }  

  这是一个重复搜索路径的代码。由于我们的角色在不停的移动,所以怪物需要在一定时间内,改变搜索目标地点,进行二次寻径。

  寻径完成后,我们可以通过path.vectorPath得到一个path数组,里面是一个List<Vector3>类型的链表。记载着路径中的每一个点的坐标。我们可以进行逐个点的移动和处理。

  这就是一个简单的AStar寻径的过程了。


  转载请注明出处:http://blog.csdn.net/ml3947

2017-11-02 11:21:38 m0_37283423 阅读数 2550
  • Unity 值得看的500+ 技术内容列表

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


我最近一直在玩新的Unity3D机器学习系统,取得了一点进展。我想分享我发现的步骤,以获得一个新创建的Agent设置,并经过训练完成一项基本任务。 在这篇文章中,您将看到如何设置基本Agent,目的是使用增强机器学习来完成随机选择的数字。 我们将使用新的Unity ML Agent系统和tensorflow来创建和训练Agent完成任务,并讨论将其扩展到真实游戏AI的方法。

翻译自外国文章看参考

设置 Tensorflow和UnityML

如果您尚未安装 并设置tensorflow ,你需要按照下属链接设置:
https://unity3d.college/2017/10/25/machine-learning-in-unity3d-setting-up-the-environment-tensorflow-for-agentml-on-windows-10/

AgentML场景设置

一旦你完成了这个过程,打开Unity项目并创建一个新的场景。

我们需要的第一件事就是academy。 创建一个新的gameobject,将其命名为“NumberAcademy”。

将“TemplateAcademy”组件添加到“NumberAcademy”中。 我们的设置不需要Academy做任何特别的事情,所以我们可以从模板中提供的基础空白学院开始。
 



在Academy下,创建另一个子游戏对象。 将其命名为“NumberBrain”。

添加一个大脑组件。

将State&Action大小变量设置为2。

将动作空间类型设置为离散。 我们将在我们的项目中使用2个离散的动作(向上或向下)。 我们使用离散量因为它们被表示为整数。

将状态空间类型设置为连续。 我们将跟踪两个浮点数状态,所以我们使用连续变量。
 


将大脑类型设置为“玩家”

添加2个动作。 选择你想要的任何2个键(我和A&B一起去),但是将值设置为0和1.绑定到值0的键递减#,绑定到1的键将增加它。
 


NumberDemoAgent脚本

创建一个名为NumberDemoAgent.cs的新脚本

将基类设置为代理(将:MonoBehaviour替换为:代理)

添加以下字段:
public class NumberDemoAgent : Agent
{
        [SerializeField]
        private float currentNumber;
        [SerializeField]
        private float targetNumber;
        [SerializeField]
        private Text text;
        [SerializeField]
        private Transform cube;
        [SerializeField]
        private Transform sphere;
        int solved;
view rawNumberDemoAgent-Fields.cs hosted with ❤ by GitHub
}
currentNumber和targetNumber字段在这里是最重要的。 一切都只是为了调试和可视化。

我们的代理将选择一个随机目标数,并尝试使用我们的递增和递减命令获取currentNumber到我们的目标。

接下来我们需要重写这个CollectState方法:

public override List<float> CollectState()
        {
                List<float> state = new List<float>();
                state.Add(currentNumber);
                state.Add(targetNumber);
                return state;
        }
view rawNumberDemoAgent-CollectState.cs hosted with ❤ by GitHub
在这里,我们正在将我们的两个浮点数作为我们Agent状态返回当前和目标号。 请注意,如何与大脑上的2个状态变量相匹配,并且它们都是浮点数,这就是为什么我们将其设置为连续状态而不是离散的。

对于我们的Agent来说,我们需要选择随机的目标号码。 为此,我们将覆盖AgentReset()方法,如下所示:
public override void AgentReset()
        {
                targetNumber = UnityEngine.Random.RandomRange(-1f, 1f);
                sphere.position = new Vector3(targetNumber * 5, 0, 0);
                currentNumber = 0f;
        }
view rawNumberDemoAgent-AgentReset.cs hosted with ❤ by GitHub

我们需要的最后和最重要的部分是AgentStep()方法。 这是我们要采取的行动(又称输入),执行一些任务(响应行动)的地方,并引导我们的Agent做出成功的选择。
public override void AgentStep(float[] action)
        {
                if (text != null)
                        text.text = string.Format("C:{0} / T:{1} [{2}]", currentNumber, targetNumber, solved);
                switch ((int)action[0])
                {
                        case 0:
                                currentNumber -= 0.01f;
                                break;
                        case 1:
                                currentNumber += 0.01f;
                                break;
                        default:
                                return;
                }
                cube.position = new Vector3(currentNumber * 5f, 0f, 0f);
                if (currentNumber < -1.2f || currentNumber > 1.2f)
                {
                        reward = -1f;
                        done = true;
                        return;
                }
                float difference = Mathf.Abs(targetNumber - currentNumber);
                if (difference <= 0.01f)
                {
                        solved++;
                        reward = 1;
                        done = true;
                        return;
                }
        }
view rawNumberDemoAgent-AgentStep.cs hosted with ❤ by GitHub



你会看到的第一件事是我们的文本更新。这仅用于调试/可视化。它可以让我们看到当前的#,目标和我们成功解决问题的次数(达到目标号码)。

接下来是我们看待行动并执行我们任务的开关。在这种情况下,我们通过递减当前数字来响应动作0,或者通过递增来响应动作1。任何价值不应该发生,但如果我们得到一个,我们只是忽略它并返回。

然后,我们根据currentNumber移动我们的立方体(使用它为x偏移量)。这个立方体只是为了可视化,它对实际的逻辑或训练没有影响。

然后我们根据一些已知的限制检查currentNumber。因为我们选择-1和1之间的随机数,如果我们达到-1.2或+1.2,我们可以认为它是一个失败,因为它肯定是错误的方向。在这种情况下,我们将奖励设置为-1表示失败,然后将其标记为true,以便代理可以重置并重试。

最后,我们检查一下currentNumber是否在目标的0.01以内。 如果是这样,我们认为这是一场比赛,将奖励设为1.0,取得成功,并将其标记为已完成。 我们也增加了解决的计数器进行调试(很高兴看到它成功了多少次)。

这是完整的脚本:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class NumberDemoAgent : Agent
{
        [SerializeField]
        private float currentNumber;
        [SerializeField]
        private float targetNumber;
        [SerializeField]
        private Text text;
        [SerializeField]
        private Transform cube;
        [SerializeField]
        private Transform sphere;
        int solved;
        public override List<float> CollectState()
        {
                List<float> state = new List<float>();
                state.Add(currentNumber);
                state.Add(targetNumber);
                return state;
        }
        public override void AgentStep(float[] action)
        {
                if (text != null)
                        text.text = string.Format("C:{0} / T:{1} [{2}]", currentNumber, targetNumber, solved);
                switch ((int)action[0])
                {
                        case 0:
                                currentNumber -= 0.01f;
                                break;
                        case 1:
                                currentNumber += 0.01f;
                                break;
                        default:
                                return;
                }
                cube.position = new Vector3(currentNumber * 5f, 0f, 0f);
                if (currentNumber < -1.2f || currentNumber > 1.2f)
                {
                        reward = -1f;
                        done = true;
                        return;
                }
                float difference = Mathf.Abs(targetNumber - currentNumber);
                if (difference <= 0.01f)
                {
                        solved++;
                        reward = 1;
                        done = true;
                        return;
                }
        }
        public override void AgentReset()
        {
                targetNumber = UnityEngine.Random.RandomRange(-1f, 1f);
                sphere.position = new Vector3(targetNumber * 5, 0, 0);
                currentNumber = 0f;
        }
}
view rawNumberDemoAgent.cs hosted with ❤ by GitHub


设置Agent
准备好脚本后,我们需要创建一个新的游戏对象并将其命名为“NumberDemoAgent”。

将NumberDemoAgent脚本附加到它并分配大脑。
 



接下来创建一个Text对象并将其放在可以看到的位置(理想情况下在屏幕中间很大)。

将文本对象分配给NumberDemoAgent。

创建一个立方体和一个球体,并将它们分配给NumberDemoAgent(这些将帮助您看到发生了什么,比阅读#更容易)。

在玩家模式下测试

现在按播放 你应该可以用你的两个热键左右移动多维数据集(记住我用A&B的热键)。

当你把球送到球体时,它应该增加解决的计数并重置。 如果你走得太远,错误的方式也应该重置(记住1.2限制)。

训练

一旦它在播放器模式下工作,选择大脑并将“大脑类型”更改为“外部”

保存场景并构建可执行文件,其中包含的场景是唯一的(启用了调试模式)。
 



对于您的输出文件夹,选择您的ml-agents项目的python子目录(包含在您下载或克隆源项目时)。 例如,我的位于这里:C:\ ml-agents \ python。
将目录更改为刚构建的python文件夹。 ex. “cd c:\ml-agents\python”

Anaconda / Jupyter

输入命令“jupyter notebook”(您可能需要按第二次输入btw)

不久之后应该提示您的Web界面,如下所示:
 



更改突出显示的部分以匹配。在env_name上,不要仅仅放入“numberdemo”,使用你构建可执行文件的名称,Buffer_size和batch_size。你可以复制(重要的是要注意,这些#只是通过测试/尝试
发现,即使在工作之后,我仍然几乎不了解他们发生了什么)。

完成编辑超级参数后,请按顺序运行步骤。

从步骤1和2开始。*消失,#完成后,
  • 出现。

  • 当你运行步骤3,你应该看到一个窗口出现在你的游戏(一个小窗口)。第一次,你也可以获得一个Windows权限对话框,确保允许它。

    一旦你开始步骤4 ...等待..并观察结果进来(第一个可能需要一分钟,以便耐心等待)
     



    一旦保存了几次,点击停止按钮。然后转到步骤5并运行它。这将导出您的训练数据到“python / models / ppo”子文件夹中的.bytes文件。



     
    复制.bytes文件(再次命名为匹配您的可执行文件名称),并将其放在您的Unity项目中。

    选择大脑并将“大脑类型”设置为“内部”。

    将.bytes文件分配给“图形模型”字段。
     


    结论

    这是一个非常简单的示例,旨在帮助您了解该系统的工作原理。 我很高兴看到你们可以建立更大的更有趣的项目来控制游戏AI,并制作有趣的游戏/漫游器。


    参考网址









    2014-01-04 10:21:59 cjc_hoderxx123 阅读数 573
    • Unity 值得看的500+ 技术内容列表

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

    电子游戏中玩家与系统进行互动的乐趣绝大多数取决于事件发生的不可预知性和随机性。在unity3DAPI中提供了Random类来解决随机问题。

    最简单的应用就是在数组中随机选择一个元素,使用Random内置的方法可以进行选择。代码如下:

    Floatelement=myArray[Random.Range(0,myArray.Length)];

    Random.Range方法的两个参数代表了选择数据元素的范围。

    很多时候,游戏中运用更复杂的随机事件,下面描述几种常见的随机事件案例

    1:随机选择具有不同概率的事件

    当你你需要选择具有不同发生概率的事件元素时。运用的场景为在游戏中玩家遇到NPC时,NPC在系统内部有好几种与玩家交互的方法。但是这些方法发生的概率并不相等。例如有50%几率进行问好;25%几率跑开;20%几率攻击玩家;5%几率为玩家提供礼品。

    学过概率统计学的同学,一定做过在连续时间内发生事件概率的模型。我们也可以将上面描述的需求抽象为一个模型,我们将整个NPC与玩家交互系统看做一个纸带,按照事件发生的概率将纸带分为若干段,将大概率事件排在前面,小概率事件排在后面。当玩家遇到NPC时代表在这个纸带上随机选择一个点。抽象结果用图如下:

    随机选择的点在哪个事件范围内就会发生相应的事件。将这个模型用代码表示为:

    float Choose(float[] Probs)
    {
    //将事件元素加入到数组中,如上面有4个元素,分别为50,25,20,5
        {
            float total=0;
            Foreach(float elem in probs )
            Total+=elem;
    }
    //Random.value方法返回一个0—1的随机数
    float randomPoint=Random.value*total;
    for(int i=0;i<pros.Length;i++ )
    {
        if(randomPoint<probs[i])
        return i;
        else
        randomPoint-=probs[i];
    }
    return probs.length-1;
        }

    过程描述:首先检查随机数是否小于第一个元素,如果小于那么第一个元素就被选择了。否则用随机数的值减去第一个元素的值然后与第二个元素进行比较,以此类推直到找到正确的元素为止。

    过程验证:如上图所示,假如随机数最终为82,那么首先将82与第一个元素50比较:82>50,因此应该将82-50=32与第二个元素25进行比较:32>25,因此应该将32-25=7与第三个元素20进行比较:7<20,那么第三个元素被选中,相对应的事件发生。

    2:使用随机数打乱数组顺序

    这个案例应用的最好场景就是纸牌游戏中的洗牌操作,思想也比较简单,就是依次将数组内的每个元素与一个随机序号的元素进行交换。代码如下:

    void Shuffle(int[] cards)
    {
        for(i=0;i<cards.Length;i++)
        {
            int temp=cards[i];
            int randomIndex=Random.Range(0,cards.Length);
            cards[i]=cards[randomIndex];
            cards[randomIndex]=temp;
            }
    }


    3:使用随机数在数组中无重复的选择元素

    在概率论中我们常常见到这样题目:一个球队10个人,要选择5个人去参加比赛。在游戏中的应用场景为你希望在几个不同的点随机生成一个不相同的NPC。例如需要在10个NPC中随机选择5个,第一个NPC被选中的概率为5/10,如果第一个被选中了那么第二个被选中的概率就变成了4/9,如果第一个被淘汰那么第二个被选中的概率就为5/9

    Transform[] spawnPoints;
    Transform[] ChooseSet(int numRequired)
    {
        Transform [] result=new Transform[numRequired];
        Int numToChoose=numRequired;
        for(int numLeft=spawnPoints.Length;numLeft>0;numLeft--)
        {
            float prob=(numToChoose+0.0)/(numLeft+0.0);
            if(Random.value<=prob)
            {
                numToChoose--;
                result[numToChoose]=spawnPoints[numLeft-1];
                if(numToChoose==0)
                break;
        }
    }
    return result;
    }

    过程描述:扫描待选择的NPC数组,如果生成的随机数小于被选中的概率则表示该元素被选中,直到所需要的元素个数都被选中。

    注意点:进行选择的时候应该先将待选择的NPC数组进行“洗牌“操作。


    没有更多推荐了,返回首页