01 using UnityEngine;
02 using System.Collections;
03
04 public class MyNewScript:MonoBehaviour
05 {
06 public string PlayerName = "";
07 public int PlayerHealth = 100;
08 public Vector3 Position = Vector3.zero;
09
10 Use this for initialization
11 void Start {
12
13 }
14
15 Update is called once per frame
16 void Update {
17
18 }
19 }
01 using UnityEngine;
02 using System.Collections;
03
04 public class MyScriptFile:MonoBehaviour
05 {
06 public string PlayerName="";
07 public int PlayerHealth=100;
08 public Vector3 Position=Vector3.zero;
09
10 Use this for initialization
11 void Start {
12 }
13
14 Update is called once per frame
15 void Update
16 {
17 Check player health-the braces symbol {} are option
for one-line if-statements
18 ifPlayerHealth == 100
19 {
20 Debug.Log "Player has full health";
21 }
22 }
23 }
上述代码的执行过程与Unity中的其他代码类型并无两样针对活动场景中的某一对象,当脚本文件实例化后,可单击工具栏中的Play按钮。其中,第18行的if语句针对当前值检测PlayerHealth类变量。如果变量PlayerHealth等于(==)100,则执行{}中的代码(第19~21行)。对应的工作流程可描述为:全部条件结果表示为布尔值true或false,经检测后可查看对应结果是否为true(PlayerHealth == 100)。实际上,花括号中可包含大量的内容,而此处仅涉及一项功能,即Unity中的Debug.Log函数向控制台输出Player has full health信息(第20行代码),如图1-6所示。当然,if语句还可包含其他分支,例如,如果PlayerHealth值不等于100(99或101),则代码将不会输出任何信息。对应的执行流程通常取决于计算结果为true的上一条if语句。
01 using UnityEngine;
02 using System.Collections;
03
04 public class MyScriptFile:MonoBehaviour
05 {
06 Define possible states for enemy using an enum
07 public enum EnemyState {CHASE, FLEE, FIGHT, HIDE};
08
09 The current state of enemy
10 public EnemyState ActiveState=EnemyState.CHASE;
11
12 Use this for initialization
13 void Start {
14 }
15
16 Update is called once per frame
17 void Update
18 {
19 Check the ActiveState variable
20 switchActiveState
21 {
22 case EnemyState.FIGHT:
23 {
24 Perform fight code here
25 Debug.Log "Entered fight state";
26 }
27 break;
28
29
30 case EnemyState.FLEE:
31 case EnemyState.HIDE:
32 {
33 Flee and hide performs the same behaviour
34 Debug.Log "Entered flee or hide state";
35 }
36 break;
37
38 default:
39 {
40 Default case when all other states fail
41 This is used for the chase state
42 Debug.Log "Entered chase state";
43 }
44 break;
45 }
46 }
47 }
01 using UnityEngine;
02 using System.Collections;
03
04 public class MyScriptFile:MonoBehaviour
05 {
06 Array of game objects in the scene
07 public GameObject[] MyObjects;
08
09 Use this for initialization
10 void Start
11 {
12 }
13
14 Update is called once per frame
15 void Update
16 {
17 }
18 }
01 using UnityEngine;
02 using System.Collections;
03
04 public class MyScriptFile:MonoBehaviour
05 {
06 Array of game objects in the scene
07 public GameObject[] MyObjects;
08
09 Use this for initialization
10 void Start
11 {
12 Build the array manually in code
13 MyObjects = new GameObject[3];
14 Scene must have a camera tagged as MainCamera
15 MyObjects[0] = Camera.main.gameObject;
16 Use GameObject.Find function to
17 find objects in scene by name
18 MyObjects[1] = GameObject.Find"Cube";
19 MyObjects[2] = GameObject.Find"Cylinder";
20 }
21
22 Update is called once per frame
23 void Update
24 {
25 }
26 }
01 using UnityEngine;
02 using System.Collections;
03
04 public class MyScriptFile:MonoBehaviour
05 {
06 Array of game objects in the scene
07 public GameObject[] MyObjects;
08
09 Use this for initialization
10 void Start
11 {
12 Repeat code for all objects in array, one by one
13 foreachGameObject Obj in MyObjects
14 {
15 Destroy object
16 Destroy Obj;
17 }
18 }
19
20 Update is called once per frame
21 void Update
22 {
23 }
24 }
01 using UnityEngine;
02 using System.Collections;
03
04 public class MyScriptFile:MonoBehaviour
05 {
06 Use this for initialization
07 void Start
08 {
09 Will count how many messages have been printed
10 int NumberOfMessages = 0;
11
12 Loop until 5 messages have been printed to the console
13 whileNumberOfMessages
14 {
15 Print message
16 Debug.Log "This is Message:"
NumberOfMessages.ToString;
17
18 Increment counter
19 NumberOfMessages;
20 }
21 }
22
23 Update is called once per frame
24 void Update
25 {
26 }
27 }
01 using UnityEngine;
02 using System.Collections;
03
04 public class MyScriptFile:MonoBehaviour
05 {
06 Private variable for score
07 Accessible only within this class
08 private int Score = 0;
09
10 Use this for initialization
11 void Start
12 {
13 Call update score
14 UpdateScore5, false; Add five points
15 UpdateScore10, false; Add ten points
16 int CurrentScore = UpdateScore 15, false; Add fifteen
points and store result
17
18 Now double score
19 UpdateScoreCurrentScore;
20 }
21
22 Update is called once per frame
23 void Update
24 {
25 }
26
27 Update game score
28 public int UpdateScore int AmountToAdd, bool
PrintToConsole = true
29 {
30 Add points to score
31 Score= AmountToAdd;
32
33 Should we print to console?
34 ifPrintToConsole{Debug.Log "Score is: "
Score.ToString;}
35
36 Output current score and exit function
37 return Score;
38 }
39 }
01 using UnityEngine;
02 using System.Collections;
03
04 public class MyScriptFile:MonoBehaviour
05 {
06 Use this for initialization
07 void Start
08 {
09 }
10
11 Update is called once per frame
12 void Update
13 {
14 Rotate object by 2 degrees per frame around the Y axis
15 transform.Rotatenew Vector30.0f, 2.0f, 0.0f;
16 }
17 }
图1-10
事件类型种类繁多,Unity中某些较为常见的事件,例如Start和Update,位于MonoBehaviour类中。关于MonoBehaviour类的更多信息,读者可访问http:docs. unity3d. com ScriptReferenceMonoBehaviour.html。
1.10 类和面向对象程序设计 类表示为多个相关变量和函数的混合产物,全部结果形成一个自包含的单元。从另一个角度来看,当考察一款游戏时(例如RPG游戏),其中往往包含了大量的独立事物,例如法师、半兽人、树木、房屋、玩家、任务道具、库存物品、武器装备、法术技能、门廊、桥梁、力场、入口、士兵等。大多数对象也可在现实世界中见到。严格地讲,此类事物均为独立的对象,例如,法师与力场截然不同且彼此独立;士兵不同于树木且二者间并不相关。对此,各项事物可视为具有自定义类型的对象。当关注于某一特定对象时,例如敌方的半兽人角色,则可在该对象中确定其属性和行为。相应地,半兽人对象包含了位置、旋转以及缩放状态等内容,并与多个变量对应。
除此之外,半兽人对象还可包含多帧攻击行为,其中包括手持斧子的近战型攻击,以及采用弓弩的远程攻击,各种攻击行为可通过函数予以施展。通过这一方式,变量和函数集合构成了一种具有一定意义的关系,这一整合方式称作封装。在当前示例中,半兽人封装至一个类中,该类定义了一个通用、抽象的半兽人模板(即半兽人这一概念)。相比之下,对象则表示为关卡中特定的Orc类实例。在Unity中,脚本文件负责定义类。当作为关卡中的一个对象实例化该类时,需要将其添加至GameObject中。如前所述,类将作为组件绑定至游戏对象上。相应地,组件定义为对象,且多个组件整体形成了GameObject。示例代码1-10显示了Orc类。
示例代码1-10
01 using UnityEngine;
02 using System.Collections;
03
04 public class Orc:MonoBehaviour
05 {
06 Reference to the transform component of orc position,
rotation, scale
07 private Transform ThisTransform = null;
08
09 Enum for states of orc
10 public enum OrcStates {NEUTRAL, ATTACK_MELEE, ATTACK_RANGE};
11
12 Current state of orc
13 public OrcStates CurrentState = OrcStates.NEUTRAL;
14
15 Movement speed of orc in meters per second
16 public float OrcSpeed = 10.0f;
17
18 Is orc friendly to player
19 public bool isFriendly = false;
20
21 --------------------------------------------------
22 Use this for initialization
23 void Start
24 {
25 Get transform of orc
26 ThisTransform = transform;
27 }
28 --------------------------------------------------
29 Update is called once per frame
30 void Update
31 {
32 }
33 --------------------------------------------------
34 State actions for orc
35 public void AttackMelee
36 {
37 Do melee attack here
38 }
39 --------------------------------------------------
40 public void AttackRange
41 {
42 Do range attack here
43 }
44 --------------------------------------------------
45 }
01 using UnityEngine;
02 using System.Collections;
03
04 public class NewScript:MonoBehaviour
05 {
06 --------------------------------------------------
07 Use this for initialization
08 void Start
09 {
10 name = "NewObject";
11 }
12 --------------------------------------------------
13 Update is called once per frame
14 void Update
15 {
16 }
17 }
01 using UnityEngine;
02 using System.Collections;
03
04 public class MyCharacter
05 {
06 public string CharName = "";
07 public int Health = 100;
08 public int Strength = 100;
09 public float Speed = 10.0f;
10 public bool isAwake = true;
11
12 Offer greeting to the player when entering conversation
13 public virtual void SayGreeting
14 {
15 Debug.Log "Hello, my friend";
16 }
17 }
对于角色在游戏中的工作方式,第一个问题则与MyCharacter类多样性和可信度相关。特别地,当SayGreeting函数被调用时,实例化自MyCharacter的各个角色具有相同的问候语Hello, my friend,其中包括男人、女人、半兽人等。该结果缺乏可信度,也并非是期望中的结果。一种可能的做法是向类中添加public字符串变量,并定制所输出的消息。然而,为了清晰地表述多态机制,下面尝试采用一种不同的解决方案。相反,这里可创建多个附加类,且均继承自MyCharacter类。其中,各个类表示为一个新的NPC类型,并借助于SayGreeting函数提供不同的问候语。这对于MyCharacter类是可行的SayGreeting函数采用关键字virtual予以声明(参见第13行代码)。这使得继承类可覆写MyCharacter类中的SayGreeting行为。因此,继承类中的SayGreeting函数将替换基类中原始函数的操作行为,如示例代码1-13所示。
示例代码1-13
01 using UnityEngine;
02 using System.Collections;
03 -------------------------------------------
04 public class MyCharacter
05 {
06 public string CharName = "";
07 public int Health = 100;
08 public int Strength = 100;
09 public float Speed = 10.0f;
10 public bool isAwake = true;
11
12 Offer greeting to the player when entering conversation
13 public virtual void SayGreeting
14 {
15 Debug.Log "Hello, my friend";
16 }
17 }
18 -------------------------------------------
19 public class ManCharacter: MyCharacter
20 {
21 public override void SayGreeting
22 {
23 Debug.Log "Hello, I''m a man";
24 }
25 }
26 -------------------------------------------
27 public class WomanCharacter: MyCharacter
28 {
29 public override void SayGreeting
30 {
31 Debug.Log "Hello, I''m a woman";
32 }
33 }
34 -------------------------------------------
35 public class OrcCharacter: MyCharacter
36 {
37 public override void SayGreeting
38 {
39 Debug.Log "Hello, I''m an Orc";
40 }
41 }
42 -------------------------------------------
01 using UnityEngine;
02 using System.Collections;
03
04 public class Tavern:MonoBehaviour
05 {
06 Array of NPCs in tavern
07 public MyCharacter[] Characters = null;
08 -------------------------------------------------------
09 Use this for initialization
10 void Start {
11
12 New array - 5 NPCs in tavern
13 Characters = new MyCharacter[5];
14
15 Add characters of different types to array MyCharacter
16 Characters[0] = new ManCharacter;
17 Characters[1] = new WomanCharacter;
18 Characters[2] = new OrcCharacter;
19 Characters[3] = new ManCharacter;
20 Characters[4] = new WomanCharacter;
21
22 Now run enter tavern functionality
23 EnterTavern;
24 }
25 -------------------------------------------------------
26 Function when player enters Tavern
27 public void EnterTavern
28 {
29 Everybody say greeting
30 foreachMyCharacter C in Characters
31 {
32 call SayGreeting in derived class
33 Derived class is accessible via base class
34 C.SayGreeting;
35 }
36 }
37 -------------------------------------------------------
38 }
01 using UnityEngine;
02 using System.Collections;
03 ------------------------------------------------------
04 Sample class - can be attached to object as a component
05 public class Database:MonoBehaviour
06 {
07 ------------------------------------------------------
08 Public property for private variable iMyNumber
09 This is a public property to the variable iMyNumber
10 public int MyNumber
11 {
12 Called when retrieving value
13 get
14 {
15 return iMyNumber; Output iMyNumber
16 }
17
18 Called when setting value
19 set
20 {
21 If value is within 1-10, set number else ignore
22 ifvalue = 1 && value
23 {
24 Update private variable
25 iMyNumber = value;
26
27 Call event
28 NumberChanged;
29 }
30 }
31 }
32 ------------------------------------------------------
33 Internal reference a number between 1-10
34 private int iMyNumber = 0;
35 ------------------------------------------------------
36 Use this for initialization
37 void Start
38 {
39 Set MyNumber
40 MyNumber = 11; Will fail because number is 10
41
42 Set MyNumber
43 MyNumber = 7; Will succeed because number is between 1-10
44 }
45 ------------------------------------------------------
46 Event called when iMyNumber is changed
47 void NumberChanged
48 {
49 Debug.Log"Variable iMyNumber changed to : "
iMyNumber.ToString;
50 }
51 ------------------------------------------------------
52 }
53 ------------------------------------------------------
01 using UnityEngine;
02 using System.Collections;
03
04 public class MyClass:MonoBehaviour
05 {
06 Will always show
07 public int PublicVar1;
08
09 Will always show
10 [SerializeField]
11 private int PrivateVar1;
12
13 Will show only in Debug Mode
14 private int PrivateVar2;
15
16 Will show only in Debug Mode
17 private int PrivateVar3;
18 }
01 using UnityEngine;
02 using System.Collections;
03
04 public class MyClass:MonoBehaviour
05 {
06 void start
07 {
08 Will invoke MyFunction on ALL componentsscripts
attached to this object where the function is present
09 SendMessage"MyFunction",
SendMessageOptions.DontRequireReceiver;
10 }
11
12 Runs when SendMessage is called
13 void MyFunction
14 {
15 Debug.Log "hello";
16 }
17 }
示例代码1-16的部分解释内容如下所示。
q 第09行代码:调用SendMessage函数时,MyFunction函数将随之被调用。MyFunction函数不仅在当前类中被调用,还将在与GameObject绑定的其他组件上进行调用(如对应组件定义了MyFunction成员函数),例如Transform组件等。
q 第09行代码:如果MyFunction不存在于某一组件中,参数SendMessageOptions. DontRequireReceiver负责确定随后的行为。此处,Unity将忽略该组件,并移至下一个组件的MyFunction函数调用。
当函数隶属于某个类中,术语函数和成员函数具有相同的含义。特别地,隶属于某一个类的函数称作成员函数。
在前述内容中曾讨论到,SendMessage方法在与独立GameObject绑定的全部组件中调用特定的函数。BroadcastMessage方法进一步整合了SendMessage方法,并针对GameObject上的全部组件调用特定的函数,并于随后针对场景层次结构中的全部子对象通过递归方式重复这一过程,直至全部子对象。
关于 SendMessage和BroadcastMessage方法的更多信息,读者可访问http:docs. unity3d.comScriptReferenceGameObject.SendMessage.html和http:docs.unity3d. comScriptReferenceComponent.BroadcastMessage.html。
SendMessage和BroadcastMessage方法简化了对象间的通信,以及组件间的通信机制。也就是说,必要时,可使组件间彼此通信,同步操作行为并复用各项功能。SendMessage和BroadcastMessage方法依赖于C#语言中的反射(reflection)机制。当采用字符串调用某一函数时,应用程序需要在运行期内对自身加以考察,针对相关功能搜索其代码。与常规方式的函数运行过程相比,这将显著地增加计算开销。对此,可搜索并最小化SendMessage和BroadcastMessage方法应用,特别是在Update事件,或者与帧相关的解决方案中。当然,这并不意味着拒绝使用反射机制,少量、适宜的操作不会对最终结果产生任何影响。后续章节将讨论其替代方案,即委托类和接口机制。
读者可参考下列书籍,以获取与C#语言相关的更多信息:
q Learning C# by Developing Games with Unity 3D Beginner''s Guide, Terry Norton, Packt Publishing。
q Intro to C# Programming and Scripting for Games in Unity, Alan Thorn(其视频教程位于https:www.udemy.com3dmotiveintro-o-c-programming-and-scripting-for- games-in-unity)。
q Pro Unity Game Development with C#, Alan Thorn, Apress。
相关的在线资源如下所示:
q http:msdn.microsoft.comen-gblibraryaa288436%28v=vs.71%29. aspx。
q http:www.csharp-station.comtutorial.aspx。
q http:docs.unity3d.comScriptReference。
1.18 本 章 小 结 本章整体讨论了Unity中的C#语言部分,针对游戏开发阐述了较为常用的语言特性。后续章节将通过更为高级的方式回顾此类话题,有助于理解并编写代码。本章内容应引起读者足够的重视。