unity学习——c#初级编程

1.作为行为组件的脚本

(Unity 中的脚本是什么?了解作为 Unity 脚本的行为组件,以及如何创建这些脚本并将它们附加到对象)

首先新建一个cube立方体

 

 

然后新建一个c#脚本,脚本用来实现立方体cube的三种颜色变化(按键实现

 

 脚本代码如下:

using UnityEngine;
using System.Collections;

public class color : MonoBehaviour
{
void Update()
{
if (Input.GetKeyDown(KeyCode.R))
{
GetComponent<Renderer>().material.color = Color.red;
}
if (Input.GetKeyDown(KeyCode.G))
{
GetComponent<Renderer>().material.color = Color.green;
}
if (Input.GetKeyDown(KeyCode.B))
{
GetComponent<Renderer>().material.color = Color.blue;
}
}
}

 

之后将脚本挂载到cube立方体上,如果出现下图问题

 

 请将c#脚本中color改成你设置的脚本名称

 

 

最后保存运行,效果如下:

R(red)

 

G(green)

 

 

B(blue)

在运行过程中,点击下图所示的元素0

 

 

 可以在下图界面通过调整Main Color更加细节的调整想要的颜色

 

 

 

 

 

 

2.变量和函数

(什么是变量和函数、它们如何为我们存储和处理信息)

我们可以编写一个data的脚本,并创建一个空对象,将data脚本挂载到空对象上,运行后,可以在控制台上看到设定好的数据

 

 脚本代码:

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

public class data : MonoBehaviour
{
int myInt = 5;

void Start()
{
Debug.Log(myInt);
}

}

 

 

上述代码中的start函数是一个不返回任何结果的函数,它的返回类型是void

当一个脚本绑定的对象进入场景时,start函数就会被调用

编写函数时,我们可以指定一个特定的返回类型
我们可以使用Debug.log来获取游戏中任意变量的值
 
例:利用调用函数来记录数值

 

 脚本代码:

using UnityEngine;
using System.Collections;

public class data : MonoBehaviour
{
int myInt = 5;


void Start()
{
myInt = MultiplyByTwo(myInt);
Debug.Log(myInt);
}


int MultiplyByTwo(int number)
{
int ret;
ret = number * 2;
return ret;
}
}

 

 

 

3.约定和语法

了解编写代码的一些基本约定和语法:点运算符、分号、缩进和注释 

句点运算符(dot operator)

  • 句点运算符是一个句点或句号,常用于代码内的单词之间,它的作用就像是编写一行地址
  • 句点运算法能让我们能有效分割或访问Unity中复合项的各个元素,复合项指包含多个元素的项

 

分号(semicolon)

  • 分号的作用是终止语句
  • 通常花括号中间的语句末尾都要加上分号。

 

缩进(indenting):

  • 缩进是代码编写的重要组成部分,它另代码清晰易读,技术角度上,缩进代码不是必需的。
  • 缩进代码能显示代码的功能结构,它令代码清晰易读
  • 通常情况下,Visual studio会帮我们自动设置缩进,但为了避免发生操作失误或者误按了TAB键等情况,程序员应该确保所有缩进都符合规定,这点在与他人合作编写代码时尤为重要。
 
注释(comment):
  • 注释的作用是写下关于某一段代码的注释给自己留下备注或提醒
  • 单行注释编写:双正斜杠(//)

 

 

多行注释编写:一个正斜杠(/)+一个星号(*),终止时调转顺序,一个星号(*)+一个正斜杠(/),在这个范围内可以写入任意行数的注释

 

 

 

 

 

 

 

注释的作用:①给自己或其他程序员留下备注

                      ②暂时禁用部分代码 

②例:

禁用一段if语句,在if语句前后插入起止多行注释符

 

 

 

4.IF语句

(如何使用 IF 语句在代码中设置条件)

编写代码时,经常遇到根据条件做出决策的情况,这个时候常使用if语句

例子:喝咖啡

using UnityEngine;
using System.Collections;

public class IfStatements : MonoBehaviour
{
    float coffeeTemperature = 85.0f;
    float hotLimitTemperature = 70.0f;
    float coldLimitTemperature = 40.0f;
    

    void Update ()
    {
        if(Input.GetKeyDown(KeyCode.Space))
            TemperatureTest();
        
        coffeeTemperature -= Time.deltaTime * 5f;
    }
    
    
    void TemperatureTest ()
    {
        // 如果咖啡的温度高于最热的饮用温度...
        if(coffeeTemperature > hotLimitTemperature)
        {
            // ... 执行此操作。
            print("Coffee is too hot.");
        }
        // 如果不是,但咖啡的温度低于最冷的饮用温度...
        else if(coffeeTemperature < coldLimitTemperature)
        {
            // ... 执行此操作。
            print("Coffee is too cold.");
        }
        // 如果两者都不是,则...
        else
        {
            // ... 执行此操作。
            print("Coffee is just right.");
        }
    }
}

 

5.循环(Loop)

(如何使用 For、While、Do-While 和 For Each 循环在代码中重复操作)

循环是编程中重复操作的方式,接下来介绍三种循环。

①while loop

作用:在满足条件时执行操作

例子:洗茶杯

using UnityEngine;
using System.Collections;

public class WhileLoop : MonoBehaviour
{
    int cupsInTheSink = 4;
    
    
    void Start ()
    {
        while(cupsInTheSink > 0)
        {
            Debug.Log ("I've washed a cup!");
            cupsInTheSink--;
        }
    }
}

 

②Dowhile Loop

作用:功能上大致与while loop相同,区别在于whileLoop在循环主体前检验条件,DoWhileLoop在循环主体结束时检验条件,这意味着DoWhileLoop主体至少会运行一次

语法:先是关键词do起始,然后是花括号,花括号中的代码构成了循环主体,循环主体后面是关键词while,再后面跟着条件句,注意条件句最后要加上分号(;)

           WhileLoop不使用分号,但DoWhileLoop使用分号

 例子:Hello World
using UnityEngine;
using System.Collections;

public class DoWhileLoop : MonoBehaviour 
{
    void Start()
    {
        bool shouldContinue = false;
        
        do
        {
            print ("Hello World");
            
        }while(shouldContinue == true);
    }
}

 

 ③ForLoop

 功能:利用可控数量的迭代创建循环,ForLoop会先检查循环中的条件,如果符合条件,就执行循环主体中的指令(即花括号中的代码),一个循环为一次迭代。

using UnityEngine;
using System.Collections;

public class ForLoop : MonoBehaviour
{
    int numEnemies = 3;
    
    
    void Start ()
    {
        for(int i = 0; i < numEnemies; i++)
        {
            Debug.Log("Creating enemy number: " + i);
        }
        }
}

 

 

 for循环的语法由三个参数组成

参数一:迭代子变量int i =0,它的作用是计算循环迭代次数,即循环次数

参数二:条件i<numEnemies,只有当条件为true时循环才会继续

参数三:i++,定义了每次循环中对迭代子的处理

简而言之,任何需要执行指定次数的运算都可以使用ForLoop实现。

 

6.作用域和访问修饰符

了解变量和函数的作用域和可访问性

  • 变量作用域指代码中可使用这个变量的的区域
  • 变量局限于代码中可以使用这个变量的位置,代码块通常用于定义变量作用域,用花括号表示
  • 类内定义的变量不同于函数内声明的变量,前者分配有访问修饰符 。访问修饰符是在声明变量时放在数据类型前的关键词,其用途是定义能够看到变量或函数的位置。
  • 一般而言,如果其他脚本需要访问某个变量或函数,就应该将其公开,否则就应设为私有

公开访问修饰符

  • 将变量设为公开意味着可从类外部访问这个变量,也意味着这个变量可在Inspector中的组件上显示和编辑。
  • 虽然公开变量可在组件上调整,但这并不表示公开变量不能回到代码中,在运行时通过脚本更改变量值
  • 将变量和函数设为公开也意味着可以通过其他脚本访问它们

注意:如果变量在类中初始化为默认值,它仍会被Inspector中的值覆盖,但如果这些值在函数中设置,它们则出现在Inspector中设置过变量之后,因此不会被Inspector覆盖

私有访问修饰符

  • 私有变量只能在类内编辑,在c#中,未指定访问修饰符的任意变量默认使用私有访问修饰符。
  • 最好是将所有成员变量(所有属于类而非函数的变量)都设为私有,除非需要将它们公开以满足特定需要。

 

7.Awake和Start

(如何使用Unity的两个初始化函数Awake和Start)

  • Awake和Start是在加载脚本时自动调用的两个函数,首先调用Awake,即使还未启用脚本组件也没关系,它非常适合用于在脚本与初始化之间设置任何引用。
  • Start在Awake之后调用,而且是直接在首次更新之前调用,但前提是已经启用了脚本组件,即启用脚本组件的情况下,可以用Start启动任何所需操作,这样就可以将初始化代码的任何部分延迟到真正需要的时候再运行。
  • Awake和Start帮助我们在启用脚本组件之前初始化对象的设置,而不需要将脚本拆分成几个不同的脚本。

注意:Start和Awake在一个对象绑定脚本的生命周期内只能调用一次,因此不能通过禁用和重新启用脚本来重复执行Start函数。

 

8.Update和FixedUpdate

(如何使用 Update 和 FixedUpdate函数实现每帧的更改,以及它们之间的区别)

Update函数:

  • Update是Unity中最常用的函数之一,在每个使用它的脚本中每帧调用一次,基本上只要需要变化或调整都需要使用Update来实现。
  • 非物理对象的移动,简单的计时器,输入检测等一般都在Update中完成

注意:Update并不是按固定时间调用的,如果某一帧比下一帧的处理时间长,那么Update调用的时间间隔就会不同

FixedUpdate函数:

  • FixedUpdate按固定时间调用,调用的时间间隔相同,调用FixedUpdate之后,会立即进行任何必要的物理计算,因此,任何影响刚体(即物理对象)的动作都应使用FixedUpdate执行而不是Update。
  • 在FixedUpdate循环中编写物理脚本时,最好使用力来定义移动。

Update和Unity中的许多其他特殊函数都可以在Visual Studio中迅速轻松创建,通过使用MonoBehaviour脚本编写向导即可实现,在Visual Studio中将光标放在需要插入新函数的位置,然后按Ctrl+Shift+M启动向导,在Create Script Methods窗口中勾选想要添加的各个方法名称旁边的复选框,选择“OK”按钮退出向导,这些方法就会插入到代码中了。通过这种方法使用向导有助于避免代码错误,也有助于一边学习一边了解可以使用的函数。

 

9.矢量数学

(矢量数学入门以及有关点积和叉积的信息)

在游戏开发中,我们利用向量来定义网格方向和所有其他类型的计算。

向量在两点之间绘制的线条,向量的长度称为大小。

二维向量(2D vectors)

二维向量表示二维平面上的点,它以原点为参照,即图中的(0,0)点,指向二维平面的任意点。因为从原点出发,因此有隐含方向,它由X和Y这两个坐标组成,它们分别代表X和Y轴上与0的距离。

三维向量(3D vectors)   

三维向量与二维向量的原理相同,但延伸出一个Z轴,它表示深度,x轴和z轴构成水平面,Y轴代表朝上的方向。

Unity采用左手坐标系,这意味着如果你举起左手,食指朝上,拇指朝外成L形,中指朝前,则拇指代表X轴,食指代表Y轴,中指代表Z轴

 

 

计算两个三维向量坐标之间的距离时,Unity为了方便计算,引入了一个帮助函数Vector3.magnitude

适用于三维向量的函数有很多,点积和叉积是典型的两种。

点积(Dot Product):

点积需要2个向量,根据它们计算出一个值,即标量。要计算2个向量的点积,需要取向量坐标,XY和Z值并分别相乘,然后将乘积相加得出最终结果。

 

 

 利用点积可以了解指定的2个向量的相关信息,如2个向量之间的点积为0,则表明这2个向量互相垂直。

例:创建飞行模拟器

 

 

 

 这时可以检查场景向上向量,与飞机向前向量的关系。如果2个向量互相垂直,即点积等于0的话,飞机阻力最小;随着点积正值增大,表明飞机正在爬升,我们可以增加阻力;如果点积负值增大,表明飞机正在俯冲。Unity有一个帮助函数可轻松完成点积计算(Vector3.Dot(VectorA,VectorB))

叉积(Cross Product):

 叉积以不同的方式组合2个向量,而不是产生一个标量值。叉积会计算出另一个向量。具体来说,是与原来2个向量垂直的向量。
叉积的数学表示:

 

 Unity针对叉积提供了一个帮助函数:Vector3.Cross(VectorA,VectorB)

例:确定围绕哪个轴施加扭矩来转动坦克的炮塔

假设你已知炮塔目前的朝向也知道炮塔的目标朝向,就可以对2个向量进行叉积运算,确定对哪个轴施加转动扭矩。

  

 

 

10.启用和禁用组件

(如何在运行时通过脚本启用和禁用组件)

启用和禁用Unity中的组件,只需使用“Enabled”标记。

需要记住的是,脚本也是组件,所以也可以使用.enabled标记来禁用脚本

例:开关灯

using UnityEngine;
using System.Collections;

public class EnableComponents : MonoBehaviour
{
    private Light myLight;
    
    
    void Start ()
    {
        myLight = GetComponent<Light>();
    }
    
    
    void Update ()
    {
        if(Input.GetKeyUp(KeyCode.Space))
        {
            myLight.enabled = !myLight.enabled;
        }
    }
}

 

 

11.激活游戏对象

(如何使用SetActive和activeSelf/activeInHierarchy单独处理以及在层级视图中处理场景内部游戏对象的活动状态)

SetActive函数:

在场景中激活或停用对象。

Active Self和Active in Hierarchy:
确认某个对象在场景或在层次结构中是否为活跃状态使用
 
如果使用对象层次结构,父对象可被停用,这样也会停止场景中活跃的子对象,但它仍在其层次结构中保持活跃状态。因此可以禁用个别对象,但可以使用父对象保持对于对象群的控制。
注意:当子对象由于其父对象被禁用而随之被禁用时,使用Set Active to True也不会激活子对象,要再次激活子对象就必须激活父对象。
 
例:查询状态
using UnityEngine;
using System.Collections;

public class CheckState : MonoBehaviour
{
    public GameObject myObject;
    
    
    void Start ()
    {
        Debug.Log("Active Self: " + myObject.activeSelf);
        Debug.Log("Active in Hierarchy" + myObject.activeInHierarchy);
    }
}

 

 

12.Translate和Rotate

(如何使用两个变换函数Translate和Rotate来更改非刚体对象的位置和旋转)

平移(Translate)和旋转(Rotate)是2种常用函数,用来更改游戏对象的位置和旋转。

平移(Translate):

代码:

using UnityEngine
using System.Collections;

public class TransformFunctions : MonoBehaviour
{
    void  Update()
    {
      transform.Translate(new Vector3(0,0,1));
    }

}

 

平移参数为Vector3,这个例子仅沿Z轴向下平移,所以X和Y的值都为0,每帧移动1个单位,因为它在Update函数中。

 

通常在进行平移操作中会乘以Time.deltaTime,这意味着它会按每秒多少米的速度移动,而不是每帧多少米,我们不需要输入Vector3(0,0,1),而是可以使用Vector3.forward作为快捷方式,接着可以用它乘以另一个值,这个值可以定义为一个单独的变量,这样就可以通过调整Inspector中的变量加以控制。

 

 

使物体可以通过按键进行上下移动:

using UnityEngine;
using System.Collections;

public class TransformFunctions : MonoBehaviour
{
    public float moveSpeed = 10f;void Update ()
    {
        if(Input.GetKey(KeyCode.UpArrow))
            transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
if(Input.GetKey(KeyCode.DownArrow)) transform.Translate(-Vector3.forward * moveSpeed * Time.deltaTime);

 

旋转(Rotate):

 

 

和平移一样,使用Vector3作为参数,用的是Vector3快捷方式——Vector3.up,它表示围绕哪个轴旋转,这是第一个参数。旋转量(turnspeed)是第二个参数。
注意:①这些函数作用于局部轴而非世界轴,所以使用Vector3.FORWARD或Vector3.UP时,相对的是脚本所应用到的游戏对象的轴。
           ②如果想用碰撞体移动某个对象,也就是将会产生物理作用的物体,则不应使用Translate和Rotate函数,而是应该考虑使用Physics函数。
 
 

13.Look At

(如何使用LookAt函数使一个游戏对象的变换组件面向另一个游戏对象的变换组件)

LookAt可用于让游戏对象的正向指向世界中的另一个transform。
如果我们想让镜头对准我们想要对准的对象,需要在Update使用LookAt函数来达到这个目的。

 

 在上图所示的脚本中,可以看到我们引用了想要寻找的对象,一个名为target的transform类型变量,我们可以使用transform.LookAt函数让对象看向target。回到unity界面时,我们需要将次脚本应用于摄像机,并将我们想要对准的游戏对象拖入target变量字段,运行后,摄像机持续朝向移动对象。

注意:可以在界面顶部切换Global和Local访问形式。Local:可以看到正向朝向对象

 代码:
using UnityEngine;
using System.Collections;

public class CameraLookAt : MonoBehaviour
{
    public Transform target;
    
    void Update ()
    {
        transform.LookAt(target);
    }
}

 

 

14.线性插值

在制作游戏时,有时可以在两个值之间进行线性插值。这是通过 Lerp 函数来完成的。线性插值会在两个给定值之间找到某个百分比的值。
例如:在数字 3 和 5 之间按 50% 进行线性插值以得到数字 4。这是因为 4 是 3 和 5 之间距离的 50%。
 
在 Unity 中,有多个 Lerp 函数可用于不同类型。刚才使用的示例,与之等效的将是 Mathf.Lerp 函数,如下所示:
// 在此示例中,result = 4
float result = Mathf.Lerp (3f, 5f, 0.5f);
Mathf.Lerp 函数接受 3 个 float 参数:一个 float 参数表示要进行插值的起始值,另一个 float 参数表示要进行插值的结束值,最后一个 float 参数表示要进行插值的距离。在此示例中,插值为 0.5,表示 50%。如果为 0,则函数将返回“from”值;如果为 1,则函数将返回“to”值。
 
Lerp 函数的其他示例包括 Color.Lerp 和 Vector3.Lerp。这些函数的工作方式与 Mathf.Lerp 完全相同,但是“from”和“to”值分别为 Color 和 Vector3 类型。在每个示例中,第三个参数仍然是一个 float 参数,表示要插值的大小。
color函数结果:找到一种颜色(两种给定颜色的某种混合)
Vector3函数结果:一个矢量(占两个给定矢量之间的百分比)。
示例:
Vector3 from = new Vector3 (1f, 2f, 3f);
Vector3 to = new Vector3 (5f, 6f, 7f);

// 此处 result = (4, 5, 6)
Vector3 result = Vector3.Lerp (from, to, 0.75f);
在此示例中,结果为 (4, 5, 6),因为 4 位于 1 到 5 之间的 75% 处,5 位于 2 到 6 之间的 75% 处,而 6 位于 3 到 7 之间的 75% 处。
 
使用 Color.Lerp 时适用同样的原理。在 Color 结构中,颜色由代表红色、蓝色、绿色和 Alpha 的 4 个 float 参数表示。使用 Lerp 时,与 Mathf.Lerp 和 Vector3.Lerp 一样,这些 float 数值将进行插值。
在某些情况下,可使用 Lerp 函数使值随时间平滑(用某时刻及其前后若干时刻的值进行加权平均,所得值作为该时刻的替代值以滤去小扰动的方法)。
示例代码如下:
void Update ()
{
    light.intensity = Mathf.Lerp(light.intensity, 8f, 0.5f);
}

 

如果光的强度从 0 开始,则在第一次更新后,其值将设置为 4。下一帧会将其设置为 6,然后设置为 7,再然后设置为 7.5,依此类推。因此,经过几帧后,光强度将趋向于 8,但随着接近目标,其变化速率将减慢。请注意,这是在若干个帧的过程中发生的。

如果我们不希望与帧率有关,则可以使用以下代码:

void Update ()
{
    light.intensity = Mathf.Lerp(light.intensity, 8f, 0.5f * Time.deltaTime);
}
这意味着强度变化将按每秒而不是每帧发生。
 
注意:在对值进行平滑时,通常情况下最好使用 SmoothDamp 函数(平滑阻阻尼函数)。仅当您确定想要的效果时,才应使用 Lerp 进行平滑。
 

15.Destroy

(如何在运行时使用Destroy()函数删除游戏对象和组件)

 在运行时移除游戏对象或从游戏对象移除组件,此外也可以通过延时达到相同目的。
例:销毁某个游戏对象  
using UnityEngine;
using System.Collections;

public class DestroyBasic : MonoBehaviour
{
    void Update ()
    {
        if(Input.GetKey(KeyCode.Space))
        {
            Destroy(gameObject);
        }
    }
}

引用与脚本关联的游戏对象,按空格键时,游戏对象就会被销毁(不应该销毁游戏对象,因为脚本组件也会随之被删除,二者是关联的)

为了解决上述提到的问题,应该引用另一个对象,设置一个名为other的公开变量,用来引用另一个对象,接着在Inspector中拖入另一个要使用的对象,之后运行时就可将想要销毁的对象销毁。

using UnityEngine;
using System.Collections;

public class DestroyOther : MonoBehaviour
{
    public GameObject other;
    
    
    void Update ()
    {
        if(Input.GetKey(KeyCode.Space))
        {
            Destroy(other);
        }
    }
}

 

 

也可以使用destroy命令移除组件,而不是整个游戏对象,为此,在destroy括号中使用GetComponent函数来引用组件。

例:销毁MeshRenderer组件

using UnityEngine;
using System.Collections;

public class DestroyComponent : MonoBehaviour
{
    void Update ()
    {
        if(Input.GetKey(KeyCode.Space))
        {
            Destroy(GetComponent<MeshRenderer>());
        }
    }
}

这样这个对象就不会再渲染出来,可以看到对象仍在游戏中,而且所有其他元素都在。

 

除了刚刚移除的MeshRenderer,上述所有示例都可以使用数字,作为第2个参数用来创建延时——Destroy(gameobject,3f)

按空格键后,就会出现3秒延时,接着对象会被移除,这同样适用于销毁组件。

 

 

16.GetButton 和 GetKey

(如何在Unity项目中获取用于输入的按钮或键,以及这些轴的行为或如何通过Unity Input Manager进行修改)

在unity中,GetKey或GetButton通过unity的输入类接收来自按键或操纵杆按钮的输入

两者的代码差异在于GetKey会使用KeyCode明确指定按键名称

例如:空格键表示为KeyCode.Space,这虽然适用于键盘按键,但建议使用GetButton指定你自己的控制。

 

输入管理器允许指定输入名称,然后给它指定一个键或按钮,要访问这个功能,可从顶部菜单选择“Edit”“Project Settings”找到“Input”,调用时可以用字符串来引用名称。

例如:Jump这是空格键表示的默认输入,但我们可以输入其他键或按钮代码来更改表示Jump的输入,接着当我们调用这个按钮时,可以使用字符串Jump引用名称。

 

使用GetKey或GetButton时,这些输入有3种状态,都会返回布尔值true或false。

首先是GetKey还是GetButton,根据有没有按下按钮来记录true或false。

没有按键按下时,GetButton返回false,第一次按下按键时,第一帧返回true,然后随着帧数的增加,我们按住按钮,GetButtonDown返回false,GetButton仍等于true,这样我们就能确认是否按住了按钮,当我们松开按钮时,GetButtonUp显示true,但也仅限第一帧,继续操纵,所有值都恢复为false。

整体流程:未按按钮时,所有值都为false。第一次按按钮时,可以用GetButtonDown查看这个状态,它会返回true而GetButton也会返回true。第一帧结束后,GetButtonDown恢复为false。这可以用于武器射击等。继续按住按钮,只有GetButton会返回true,接着松开按钮时,GetButtonUp返回true持续一帧,接着恢复为false。

注意:GetKey的行为完全相同,只是代码写法略有差异。

要查看按钮的状态,使用输入管理器内输入的标题字符串jump,但如果要查看特定键的状态,可以使用KeyCode,因为KeyCode只与特定键相关,建议使用GetButton,并用输入管理器指定输入。

KeyInput代码:

using UnityEngine;
using System.Collections;

public class KeyInput : MonoBehaviour
{
    public GUITexture graphic;
    public Texture2D standard;
    public Texture2D downgfx;
    public Texture2D upgfx;
    public Texture2D heldgfx;
    
    void Start()
    {
        graphic.texture = standard;
    }
     void Update ()
    {
        bool down = Input.GetKeyDown(KeyCode.Space);
        bool held = Input.GetKey(KeyCode.Space);
        bool up = Input.GetKeyUp(KeyCode.Space);
        
        if(down)
        {
            graphic.texture = downgfx;
        }
        else if(held)
        {
            graphic.texture = heldgfx;
        }
        else if(up)
        {
            graphic.texture = upgfx;
        }
        else
       {
            graphic.texture = standard; 
        }
        
        guiText.text = " " + down + "\n " + held + "\n " + up;
    }
}

 

ButtonInput:

using UnityEngine;
using System.Collections;

public class ButtonInput : MonoBehaviour
{
    public GUITexture graphic;
    public Texture2D standard;
    public Texture2D downgfx;
    public Texture2D upgfx;
    public Texture2D heldgfx;
    
    void Start()
    {
        graphic.texture = standard;
    }
void Update ()
    {
        bool down = Input.GetButtonDown("Jump");
        bool held = Input.GetButton("Jump");
        bool up = Input.GetButtonUp("Jump");
        
        if(down)
        {
            graphic.texture = downgfx;
        }
        else if(held)
        {
            graphic.texture = heldgfx;
        }
        else if(up)
        {
            graphic.texture = upgfx;
        }
 else
        {
            graphic.texture = standard;
        }
    
        guiText.text = " " + down + "\n " + held + "\n " + up;
    }
}

 

 

17.GetAxis

(如何在Unity中为游戏获取基于轴的输入,以及如何通过Input Manager修改这些轴)

GetAxis会返回浮点值,这个值介于-1到1之间,轴在输入管理器中设置。

访问方法:从顶部菜单选择“Edit”,“Project Settings”,“Input”。

按钮操作时,只需考虑“Positive Button”的值,对于轴“Positive Button”和“Negative Button”都要考虑,以及“Gravitty”,"Sensitivity","Dead"和"Snap" 。

因为GetAxis返回的是浮点值,所以它相当于是介于正负一之间的滑尺。

轴的Gravity会影响滑尺,在按钮松开后归零的速度。Gravity越高,归零速度越快。

Sensitivity与Gravity相反,它控制着输入的返回值,到达1或-1的速度有多快,Sensitivity值越大,反应速度就越快,值越小,移动越流畅。

如果用操纵杆表示轴,难免会出现操作杆轻微移动的情况,为避免这种情况,需要一个盲区。Dead值越大,盲区越大,因此操作杆的移动的幅度也必须增大才能让GetAxis返回非0值。

Snap选项的作用是同时按下正负按钮时归零,为获得横轴或竖轴的值,只需在代码中添加一个Input.GetAxis(“Horizontal”)或Input.GetAxis(“Vertical”),也可使用Input.GetAxis(“Raw”),仅返回整数不返回非整数,这十分适合需要精准控制的二维游戏,不适用于需要平滑值的游戏。

注意:Snap不需要使用Gravity或Sensitivity

 

18.OnMouseDown

(如何检测碰撞体或GUI元素上的鼠标点击)

OnMouseDown及其相关函数可检测对碰撞体或GUI文本元素的点击。

MouseClick代码:

using  UnityEngine;
using  System.Collections;

public class MouseClick : MonoBehaviour
{
    void OnMouseDown ()
    {
        rigidbody.AddForce(-transform.forward * 500f);
        rigidbody.useGravity = true;
    }
}

():给物体对象施加了一个反作用力

 

19.GetComponent

(如何使用GetComponent函数来处理其他脚本或组件的属性)

脚本被视为自定义组件,我们通常需要访问与同一个游戏对象关联的其他脚本甚至是与其他游戏对象关联的脚本。访问其他脚本和组件要使用GetComponent。

using UnityEngine;
using System.Collections;

public class UsingOtherComponents : MonoBehaviour
{
    public GameObject otherGameObject;
    
    
    private AnotherScript anotherScript;
    private YetAnotherScript yetAnotherScript;
    private BoxCollider boxCol;
    
    
    void Awake ()
    {
        anotherScript = GetComponent<AnotherScript>();
        yetAnotherScript = otherGameObject.GetComponent<YetAnotherScript>();
        boxCol = otherGameObject.GetComponent<BoxCollider>();
    }
    
    
    void Start ()
    {
        boxCol.size = new Vector3(3,3,3);
        Debug.Log("The player's score is " + anotherScript.playerScore);
        Debug.Log("The player has died " + yetAnotherScript.numberOfPlayerDeaths + " times");
    }
}

 

 在Awake函数中进行变量初始化,GetComponent函数使用的调用类型与常用的略有差异,普通括号前的尖括号,尖括号的作用是让类型成为参数。

上述代码中,类型是AnotherScript,也可以调用GetComponent来访问

虽然GetComponent最常用于访问其他脚本,但它也用于访问API未公开的其他组件

注意:GetComponent会占用大量处理能力所以应该尽量减少调用,最好是在Awake或Start函数中调用或仅在首次需要时调用一次

 

20.Delta Time

(什么是Delta Time,如何在游戏中将其用于对值进行平滑和解释)

Delta指两个值之间的差

 
using UnityEngine;
using System.Collections;

public class UsingDeltaTime : MonoBehaviour
{
    public float speed = 8f; 
    public float countdown = 3.0f;

    
    void Update ()
    {
        countdown -= Time.deltaTime;
        if(countdown <= 0.0f)
            light.enabled = true;
        
         if(Input.GetKey(KeyCode.RightArrow))
            transform.position += new Vector3(speed * Time.deltaTime, 0.0f, 0.0f);
    }   
}

 

 time类的DeltaTime属性基本上指两次更新或固定更新函数调的间隔时长。它的作用是让用于移动其他增量计算的值变得平滑。

帧与帧之间的时差不是固定的。

假设某个对象每帧移动固定距离,整体效果可能并不流畅,这是因为完成一帧所需的时间是不同的,虽然移动的距离是固定不变的。如果使用Time.deltaTime修改变化量,所需时间较长的帧变化较大;所需时间较短的帧变化较小,所以最后整体效果是使用了time类的DeltaTime属性后,在一段时间内物体的移动变化看起来很流畅。

使用了 time类的DeltaTime属性后,即使帧率变化,速度也会保持恒定。Time.deltaTime的这种用法让我们能更改每秒的值而非每帧的值。

 

21.数据类型

(了解“值”和“引用”数据类型之间的重要区别,以便更好地了解变量的工作方式)

 两种主要的数据类型为值类型(value)和引用类型(reference)

 Value:整数(int)、浮点数(float)、双精度(double)、布尔型(bool)、字符(char)、Structs(包含一个或多个其他变量,Unity中最常见的2种Structs为Vector3和Quaternion)

 Reference:任何属于类对象的变量都叫做引用类型,在unity中最常见的2个类——引用类型Transform和GameObject

值类型和引用类型区别:值类型变量包含某个值,所有引用类型变量都包含值存储位置的存储地址

因此如果值类型改变则只会影响特定变量,但如果引用类型改变,所有包含特定存储地址的变量都会受到影响。

值类型赋值其实就是复制变量,引用类型赋值需要记住所需值的存储地址,在需要时返回这个地址获取变量的值。值类型包含其自己的数据副本,更改它们只会影响特定变量。

 

 

22.类

(如何使用类来存储和组织信息,以及如何创建构造函数以便处理类的各个部分)

 在Unity中脚本可能包含类定义,打个比方:变量比作盒子,将函数比作机器,那类就相当于是这些盒子和机器所在的工厂。
类是一个容器,用来储存变量和函数,具备多种功能包括将配合工作的要素组合起来,它们是有组织结构的工具,属于面向对象编程,简称OOP(Object-Oriented-Programming),面向对象编程的原则之一是将脚本拆分成多个脚本,其中每一个脚本承担一个角色或职责,因此类非常适合于专门完成一项任务。
例:
using UnityEngine;
using System.Collections;

public class SingleCharacterScript : MonoBehaviour
{
    public class Stuff
    {
        public int bullets;
        public int grenades;
        public int rockets;
        
        public Stuff(int bul, int gre, int roc)
        {
            bullets = bul;
            grenades = gre;
            rockets = roc;
        }
    }
    
    
    public Stuff myStuff = new Stuff(10, 7, 25);
    public float speed;
    public float turnSpeed;
    public Rigidbody bulletPrefab;
    public Transform firePosition;
    public float bulletSpeed;
    
    void Update ()
    {
        Movement();
        Shoot();
    }
    
    
    void Movement ()
    {
        float forwardMovement = Input.GetAxis("Vertical") * speed * Time.deltaTime;
        float turnMovement = Input.GetAxis("Horizontal") * turnSpeed * Time.deltaTime;
        
        transform.Translate(Vector3.forward * forwardMovement);
        transform.Rotate(Vector3.up * turnMovement);
    }
    
    
    void Shoot ()
    {
        if(Input.GetButtonDown("Fire1") && myStuff.bullets > 0)
        {
            Rigidbody bulletInstance = Instantiate(bulletPrefab, firePosition.position, firePosition.rotation) as Rigidbody;
            bulletInstance.AddForce(firePosition.forward * bulletSpeed);
            myStuff.bullets--;
        }
    }
}

将上述例子中实现三个功能的一个脚本更改为三个短的脚本分别实现功能,这样做会使脚本更易于管理,更易阅读,编程效率更高。

 

 在创建实例时,new Stuff后面的括号表明使用的是构造函数,创建类或struct时都要调用构造函数,类或struct可能有多个构造函数,这些函数使用不同参数,构造函数允许程序员设置默认值限制实例化以及编写灵活易读的代码。

注意(构造函数):①构造函数的名称始终是类的名称②构造函数一定不会有返回类型,连void都不会有

                                 ③一个类可能有多个不同的构造函数,但对象初始化时只会调用其中一个构造函数

在了解了类对于创建游戏时组织数据的用处,在编写脚本时,建议先全面仔细地设计脚本结构,然后再开始编写一个大类将各种不同的内容囊括其中

 

23.Instantiate

(如何在运行期间使用 Instantiate 创建预制件的克隆体)

Instantiate函数的作用是克隆游戏对象,它常用于克隆prefab。prefab就是指项目素材中的预配置对象。

例:从发射器发出的抛射体

 每一个抛射体都需要实例化到游戏世界中,从而实现发射操作。

本例中使用Fire1来激活instantiate函数,最基本的instantiate形式只需一个参数,也就是需要克隆的对象。

在本例中,创建了一个名为projectile的公开变量,将这个变量传递到instantiate函数,但这意味着prefab将在其默认位置实例化,本例中是位置0。

将上述例子中的代码运行后,物体不能实现抛射体的效果,只会在一个固定位置生成。

更改代码如下:

using UnityEngine;
using System.Collections;

public class UsingInstantiate : MonoBehaviour
{
    public Rigidbody rocketPrefab;
    public Transform barrelEnd;
    
    
    void Update ()
    {
        if(Input.GetButtonDown("Fire1"))
        {
            Rigidbody rocketInstance;
            rocketInstance = Instantiate(rocketPrefab, barrelEnd.position, barrelEnd.rotation) as Rigidbody;
            rocketInstance.AddForce(barrelEnd.forward * 5000);
        }
    }
}

 

 注:在游戏中创建多个克隆体时,这些克隆体任会存在于场景中,使用下方脚本解决问题
using UnityEngine;
using System.Collections;

public class RocketDestruction : MonoBehaviour
{
    void Start()
    {
        Destroy (gameObject, 1.5f);
    }
}

 

24.数组

(使用数组将变量集合在一起以便于管理)

 数组的作用是储存同类型数据,数组不是类型而是特定类型的变量集合。
使用数组前,先要知道数组的长度,长度指数组其中将会储存多少个元素。数组中的元素称为项。
为了指定数组的长度,需使用关键词new,接着输入数组类型并在方括号中输入元素数量。
注:①如果设为公开数组,就能在Inspector中看到这个数组,并为它分配值。
       ②数组非常适合与循环配合使用
例:记录场景中所有玩家的名称
using UnityEngine;
using System.Collections;

public class Arrays : MonoBehaviour
{
    public GameObject[] players;

    void Start ()
    {
        players = GameObject.FindGameObjectsWithTag("Player");
        
        for(int i = 0; i < players.Length; i++)
        {
            Debug.Log("Player Number "+i+" is named "+players[i].name);
        }
    }
}

 

 

25.Invoke

(如何在 Unity 脚本中使用 Invoke、InvokeRepeating 和 CancelInvoke 函数)

Invoke函数的作用是将函数调用安排在指定延时后发生,可以借此构建对时间敏感的有效的方法调用系统。

Invoke函数有两个参数,一个是字符串,包含需要调用的方法名称,另一个是以秒为单位的延时时长

 注:只有不包含参数且返回类型为void的方法才能用Invoke调用

 例:实例化游戏对象(单个)
using UnityEngine;
using System.Collections;

public class InvokeScript : MonoBehaviour 
{
    public GameObject target;
    
    
    void Start()
    {
        Invoke ("SpawnObject", 2);
    }
    
    void SpawnObject()
    {
        Instantiate(target, new Vector3(0, 2, 0), Quaternion.identity);
    }
}

这段代码中Invoke函数只单一运行一次,会在指定位置生成一个游戏对象

如果想要生成多个游戏对象,使用InvokeRepeating函数即可

例:实例化游戏对象(多个)

using UnityEngine;
using System.Collections;

public class InvokeRepeating : MonoBehaviour 
{
    public GameObject target;
    
    
    void Start()
    {
        InvokeRepeating("SpawnObject", 2, 1);
    }
    
    void SpawnObject()
    {
        float x = Random.Range(-2.0f, 2.0f);
        float z = Random.Range(-2.0f, 2.0f);
        Instantiate(target, new Vector3(x, 2, z), Quaternion.identity);
    }
}

InvokeRepeating函数此时有三个参数,前两个和Invoke函数一样,最后一个参数指重复此函数的时间间隔(秒)

 

26.枚举

(如何在代码中声明和使用枚举)

枚举通常被称为enums,是一种特殊的数据类型,有特定的可能值子集,可用于创建相关常量的集合。
 
枚举可以在类内或类外创建,也可以创建只包含此枚举的C#脚本,不将它声明为类,而是将它声明为枚举,然后可以在其他脚本的类中使用这个枚举,因为这是公开枚举。
 
枚举放到类内的前提必须是这个类需要访问这个枚举。我们需要列出枚举的各个常量用逗号隔开,用花括号括起来。
 
枚举中声明的每个常量都有一个值,默认为从0开始往上数的整数,这些值通过集整合起来。(不赋值的情况)

 如果需要更改枚举的类型,可在枚举名称后添加一个冒号,然后在后面输入类型

例:方向枚举

using UnityEngine;
using System.Collections;

public class EnumScript : MonoBehaviour 
{
    enum Direction {North, East, South, West};

        void Start () 
    {
        Direction myDirection;
        
        myDirection = Direction.North;
    }
    
    Direction ReverseDirection (Direction dir)
    {
        if(dir == Direction.North)
            dir = Direction.South;
        else if(dir == Direction.South)
            dir = Direction.North;
        else if(dir == Direction.East)
            dir = Direction.West;
        else if(dir == Direction.West)
            dir = Direction.East;
        
        return dir;     
    }
}

 

 

27.Switch语句

(如何编写和使用 switch 语句)

在代码中做决策时,通常使用switch语句,决策依据是枚举
switch语句是更精简的条件语句,作用是将单一变量与一系列常量进行对比。
switch语句的语法:首先输入关键词switch,接着在括号中输入变量(需要对比的项)。
 
在visual studio中,可以快速创建switch语句,只需输入S-W,然后按Tab键创建代码片段

 也可以输入switch语句将要评估的变量名称,对于枚举这样的特定变量,系统会自动填充switch语句的各种实例。

 

样例脚本如下(根据智商高低游戏NPC发言):

using UnityEngine;
using System.Collections;

public class ConversationScript : MonoBehaviour 
{
    public int intelligence = 5;
    
    
    void Greet()
    {
        switch (intelligence)
        {
        case 5:
            print ("Why hello there good sir! Let me teach you about Trigonometry!");
            break;
        case 4:
            print ("Hello and good day!");
            break;
        case 3:
            print ("Whadya want?");
            break;
        case 2:
            print ("Grog SMASH!");
            break;
        case 1:
            print ("Ulg, glib, Pblblblblb");
            break;
        default:
            print ("Incorrect intelligence level.");
            break;
        }
    }
}

default与if/else语句中的else类似,default涵盖先前条件语句未涵盖的所有其他情况。

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         

 

 

posted @ 2023-03-30 23:25  不用去猜mk  阅读(709)  评论(1编辑  收藏  举报