2012-09-28 19:31:40 qn9663 阅读数 971
unity3d的协同程序(Coroutine)思考 
     最近用c#在写一个益智游戏,使用了yield,协同程序很方便,使程序变的清晰简单,也带来了问题。
在GameObject.active = false 时他会销毁你的任何一个协同程序过程,并且不会等待过程结束后销毁,GameObject.enable时他并不可以恢复。所以以后使用Coroutine时要分场合和配合正确的逻辑使用。

2017-04-28 16:02:05 yu__jiaoshou 阅读数 577

Unity 3D - 协同程序 :

API列表 :

名称 作用
StartCoroutine 启动指定协同程序
StopCoroutine 终止指定协同程序
StopAllCoroutines 终止所有协同程序
WaitForSeconds 等待若干秒
WaitForFixedUpdate 等待知道下一次FixedUpdate调用

说明 :

  • 一般用来在脚本中增加延时效果。因为在Start()或者Update()中是不能直接延时的(WaitForSecond())等待某个操作结束之后再执行代码字符串做为参数

  • 协同程序,简称“协程”. 在脚本运行过程中,需要额外的执行一些其他的代码,这个时候就可以将“其他的代码”以协程的形式来运行 .

  • 类似于开启了一个线程,但是协程不是线程。

开启协同:

  • StartCoroutine(string methodName):字符串作为参数可以开启线程并在协程结束前终止线程;开启协程时最多只能传递一个参数,并且性能消耗会更大一点

  • StartCoroutine(IEnumerator routine):只能等待协程的结束而不能随时终止(除非使用StopAllCoroutines()方法)

中止协同:

  • StopCoroutine(string methodName):中止一个协同,只能终止该MonoBehaviour中的协同程序

  • StopAllCoroutines():中止所有协同,只能终止该MonoBehaviour中的协同程序

yiled:

  • 和协同密切相关的一个概念,一个协同程序在执行过程中,可以在任意位置使用yield语句。yield的返回值控制何时恢复协同程序向下执行。

注意 :

  • 将协同程序所在gameobject的active属性设置为false,当再次设置active为ture时,协同程序并不会再开启。

  • yield不可单独使用 , 需要与return配合使用 , 例如:

    //等0帧
    yield return 0;

    //等待1帧
    yield return 1;

    //等待3秒
    yield return WaitForSeconds(3.0);

    //立即返回调用点
    yield return null;
  • 所有使用yield的函数必须将返回值类型设置为IEnumerator类型,例如:
IEnumerator DoSomething() {
    Debug.Log("do something…");
}

C#实例 :

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

public class TestScript : MonoBehaviour {

    void Start () {

        print ("begin");

        StartCoroutine ("Test");
    }

    IEnumerator Test()
    {
        yield return new WaitForSeconds (2);
        print("wait 2 seconds print");
    }
} 
2013-06-18 11:39:21 shuefmlk 阅读数 3519

有些程序或者游戏要在用户关闭之前提示用户是否确定关闭改程序以避免用户误操作导致程序关闭,所以我们写以下代码防止用户误操作导致关闭,在此之前你可以通过pop一个alertView提示用户是否确定关闭此程序。


下面是代码:分析下代码在程序被点击关闭的时候会调用所有脚本的 OnApplictonQuit函数 然后我们通过调用一个异步函数实现block2秒钟 判断是否是否再次确认结束程序 如果是 就让程序结束 否则 调用异步函数再次block两秒

Application.CancelQuit 取消退出
static function CancelQuit () : void

Description描述

Cancels quitting the application. This is useful for showing a splash screen at the end of a game.

取消退出。这可以用来在退出游戏的时候显示一个退出画面。

This function only works in the player and does nothing in the web player or editor. IMPORTANT: This function has no effect on iPhone. Application can not prevent termination under iPhone OS.

这个函数只工作在播发器中,在web播放器或编辑器中不做任何事。

注意,这个函数在iphone中没有效果,应用程序无法防止在iPhone OS的终止。

C#
JavaScript
// Delays quitting for 2 seconds and 
// 延迟2秒退出。
// loads the finalsplash level during that time.
// 在这段时间内加载退出画面
var showSplashTimeout : float = 2.0;
private var allowQuitting : boolean = false;

function Awake () {
	// This game object needs to survive multiple levels
	// 需要在多个关卡中使用的游戏物体
	DontDestroyOnLoad (this);
}

function OnApplicationQuit () {
	// If we haven't already load up the final splash screen level
	// 如果我们还没有加载到最后的退出画面
	if ( Application.loadedLevelName .ToLower() != "finalsplash")
		StartCoroutine("DelayedQuit");
   
	// Don't allow the user to exit until we got permission in 
	// 如果我们还没有加载到最后的退出画面
	if (!allowQuitting)
		Application.CancelQuit ();
}

function DelayedQuit () {
    Application.LoadLevel ("finalsplash");
   
	// Wait for showSplashTimeout
	// 等待showSplashTimecout
	yield WaitForSeconds (showSplashTimeout);   
	// then quit for real
	// 然后退出
	allowQuitting = true;
	Application.Quit ();
}

2016-03-25 10:12:09 lirentai 阅读数 6247

最近在写Editor插件,遇到一个很迷知的现象,在写编辑器的时候,发现一个Item位置不对,需要在代码里面调整下,Unity编译结束后,会发现有些变量被Unity销毁了,有些变量值仍然被保留。这个问题需要关心的目的是,如果写的是一个比较大型的插件,给别人用的时候,发现一些项目资源忘记更新了,就顺手切到目录下更新,有时候会更新到一些程序脚本,再切换回Unity的时候,Unity会自动编译,这样之前做的操作可能就被自动清空掉了,所以了解EditorWindow里面什么类型的变量会在Unity编译时候被销毁,什么类型的不会被销毁,是非常有必要的。

我测试了一下,得到的一些心得。

在EditorWindow类中的基础数据类型(int,float,string,double..),unity里面序列化的对象(Vector3,Quaternion,GameObject,Transform,继承Component,继承ScriptableObject),只要窗口不被关闭,这些变量数据不会因为程序重新编译被销毁。

但是如果这些类型的变量为静态类型,每次编译后,这个静态变量就会被销毁掉。

还有就是需要编译后不被销毁的对象不能这样写:

public int ID{get;set;}


以下是测试的代码:

using UnityEditor;
using UnityEngine;
using System.Collections;

public class TestEditor:EditorWindow {

    [MenuItem("Tools/Test Window")]
    private static void Open()
    {
        EditorWindow.CreateInstance<TestEditor>().Show();
    }
    //-------------------------------------------------------//
    private string testString;                                                      //不会销毁
    private static string testStaticString;                                         //会被销毁
    private GameObject testGameObject;                                              //不会销毁
    public static GameObject testStaticGameObject;                                  //会被销毁
    public static string testPublicStaticString;                                    //会被销毁
    private TestDataNoSerialize testNoSerializeData =new TestDataNoSerialize();     //会被销毁
    private TestDataSerialize testSerializeData = new TestDataSerialize();          //不会销毁
    private TestScriptableObject testScriptObject;                                  //不会销毁
    //------------------------------------------------------//
    private void OnGUI()
    {
        testString =EditorGUILayout.TextField("Test String", testString);
        testStaticString = EditorGUILayout.TextField("Test Static String", testStaticString);
        testPublicStaticString = EditorGUILayout.TextField("Test Public Static String", testPublicStaticString);
        testGameObject = EditorGUILayout.ObjectField("Test Game Object",testGameObject,typeof(GameObject),true) as GameObject;
        testStaticGameObject = EditorGUILayout.ObjectField("Test Static Game Object", testStaticGameObject, typeof(GameObject), true) as GameObject;
        EditorGUILayout.Space();
        EditorGUILayout.Space();
        EditorGUILayout.LabelField("No Serialize Data");
        testNoSerializeData.Name =EditorGUILayout.TextField("Name", testNoSerializeData.Name);
        testNoSerializeData.ID = EditorGUILayout.IntField("ID", testNoSerializeData.ID);
        testNoSerializeData.Obj = EditorGUILayout.ObjectField("Game Object", testNoSerializeData.Obj, typeof(GameObject), true) as GameObject;
        EditorGUILayout.Space();
        EditorGUILayout.Space();
        EditorGUILayout.LabelField("Serialize Data");
        testSerializeData.Name = EditorGUILayout.TextField("Name", testSerializeData.Name);
        testSerializeData.ID = EditorGUILayout.IntField("ID", testSerializeData.ID);
        testSerializeData.Obj = EditorGUILayout.ObjectField("Game Object", testSerializeData.Obj, typeof(GameObject), true) as GameObject;
        if (testScriptObject == null)
        {
            testScriptObject =ScriptableObject.CreateInstance<TestScriptableObject>();
            Debug.Log("Create Instance");
        }

        EditorGUILayout.Space();
        EditorGUILayout.Space();
        EditorGUILayout.LabelField("ScriptableObject Data");
        testScriptObject.Name = EditorGUILayout.TextField("Name", testScriptObject.Name);
        testScriptObject.ID = EditorGUILayout.IntField("ID", testScriptObject.ID);
        testScriptObject.Obj = EditorGUILayout.ObjectField("Game Object", testScriptObject.Obj, typeof(GameObject), true) as GameObject;
    }
}

public class TestDataNoSerialize {
    public string Name;
    public int ID;
    
    public GameObject Obj;
}

[System.Serializable]
public class TestDataSerialize
{
    public string Name;
    public int ID                                                                   //会被销毁
    {
        get; set;
    }
    public GameObject Obj;
}

public class TestScriptableObject:ScriptableObject{
    public string Name;
    public int ID;
    public GameObject Obj;
}


2018-05-11 22:50:49 mozi_momo 阅读数 318

巡逻兵游戏

  • 游戏设计要求:

    • 创建一个地图和若干巡逻兵;
    • 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算;
    • 巡逻兵碰撞到障碍物如树,则会自动选下一个点为目标;
    • 巡逻兵在设定范围内感知到玩家,会自动追击玩家;
    • 失去玩家目标后,继续巡逻;
    • 计分:每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束。
  • 程序设计要求:
    • 必须使用订阅与发布模式传消息、工厂模式生产巡逻兵

完成效果图

这里写图片描述

实现过程

代码部分有一些参考了学长们之前做的游戏,对初学者而言真的是帮助很大。在一些不够完善的细节上我加上了自己的一些设计,学到了不少东西。

在asset store里下载了可用的人物和小僵尸模型(挑了好久才挑出这么可爱的),加上动画和状态机设置让人物动起来。
人物chara的状态机如下,主要有idle和run两种状态,以及是否存活:
这里写图片描述

僵尸patrol的状态机如下,与人物相比,多了巡逻时的walk状态,当发现玩家时从walk状态变为run,速度加快;由于巡逻兵不会死亡,因此不需要增加die:
这里写图片描述

实现人物移动时,当人物与墙发生碰撞、与巡逻兵发生碰撞时都会受到外力作用,因此位置会改变。防止碰撞带来的作用部分如下:

if (this.transform.localEulerAngles.x != 0 || this.transform.localEulerAngles.z != 0){
    this.transform.localEulerAngles = new Vector3(0, this.transform.localEulerAngles.y, 0);
}
if (this.transform.position.y != 0){
    this.transform.position = new Vector3(this.transform.position.x, 0, this.transform.position.z);
} 

此外,为了使游戏看的更清楚,增加了镜头随鼠标移动的部分。鼠标滑轮控制镜头放大缩小,左键拖动控制镜头移动。
原本还加上了右键控制镜头旋转,但实际体验效果并不好,很容易操作不当,因此并没保留镜头旋转。(也可能是我单机打的太少操作太差)

using UnityEngine;
using System.Collections;
public class MoveCamera : MonoBehaviour {

    public int cameraMoveSpeed = 10;//相机移动速度
    float originalPositionX;
    float originalPositionY;
    void Start()
    {
        originalPositionX = this.transform.position.x;
        originalPositionY = this.transform.position.y;
    }
    void OnGUI()
        {
        //鼠标左键调节镜头位置
        if (Event.current.type == EventType.MouseDrag) {
            float x;
            float y;
            x = Input.GetAxis ("Mouse X");
            y = Input.GetAxis ("Mouse Y");
            transform.Translate (new Vector3 (-x, 0, 0) * Time.deltaTime * cameraMoveSpeed);
            transform.Translate (new Vector3 (0, -y, 0) * Time.deltaTime * cameraMoveSpeed);
        }
    }

    void Update()  
    {  
         //鼠标滑轮控制放大缩小
        if( Input.GetAxis("Mouse ScrollWheel") != 0 )
        {
            this.gameObject.transform.Translate(new Vector3(0,0,Input.GetAxis("Mouse ScrollWheel")*Time.deltaTime*500));
        }
    }  
}  

为了装饰游戏,我找了带有雪的栅栏、雪地的贴图和飘雪花的效果,加上了天空盒,放置了几个雪人作为障碍物用来跟巡逻兵斗智斗勇;飘落雪花的动画也很有雪地的感觉。
这里写图片描述

项目代码

完整代码按照往常一样放在了github里,地址走→https://github.com/moko-momo/patrol-game

unity3D 巡逻兵

阅读数 106

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