Unity開(kāi)啟篇(十三) —— Unity腳本簡(jiǎn)介(二)

版本記錄

版本號(hào) 時(shí)間
V1.0 2019.01.26 星期六

前言

Unity是由Unity Technologies開(kāi)發(fā)的一個(gè)讓玩家輕松創(chuàng)建諸如三維視頻游戲、建筑可視化、實(shí)時(shí)三維動(dòng)畫(huà)等類型互動(dòng)內(nèi)容的多平臺(tái)的綜合型游戲開(kāi)發(fā)工具,是一個(gè)全面整合的專業(yè)游戲引擎。Unity類似于Director,Blender game engine, Virtools 或 Torque Game Builder等利用交互的圖型化開(kāi)發(fā)環(huán)境為首要方式的軟件。其編輯器運(yùn)行在Windows 和Mac OS X下,可發(fā)布游戲至WindowsMacWiiiPhoneWebGL(需要HTML5)、Windows phone 8和Android平臺(tái)。也可以利用Unity web player插件發(fā)布網(wǎng)頁(yè)游戲,支持Mac和Windows的網(wǎng)頁(yè)瀏覽。它的網(wǎng)頁(yè)播放器也被Mac 所支持。網(wǎng)頁(yè)游戲 坦克英雄和手機(jī)游戲王者榮耀都是基于它的開(kāi)發(fā)。
下面我們就一起開(kāi)啟Unity之旅。感興趣的看下面幾篇文章。
1. Unity開(kāi)啟篇(一) —— Unity界面及創(chuàng)建第一個(gè)簡(jiǎn)單的游戲 (一)
2. Unity開(kāi)啟篇(二) —— Unity界面及創(chuàng)建第一個(gè)簡(jiǎn)單的游戲 (二)
3. Unity開(kāi)啟篇(三) —— 一款簡(jiǎn)單射擊游戲示例 (一)
4. Unity開(kāi)啟篇(四) —— 一款簡(jiǎn)單射擊游戲示例 (二)
5. Unity開(kāi)啟篇(五) —— 一款簡(jiǎn)單射擊游戲示例 (三)
6. Unity開(kāi)啟篇(六) —— Unity動(dòng)畫(huà)簡(jiǎn)介 (一)
7. Unity開(kāi)啟篇(七) —— Unity動(dòng)畫(huà)簡(jiǎn)介 (二)
8. Unity開(kāi)啟篇(八) —— Unity聲音簡(jiǎn)介(一)
9. Unity開(kāi)啟篇(九) —— Unity聲音簡(jiǎn)介(二)
10. Unity開(kāi)啟篇(十) —— Unity粒子系統(tǒng)簡(jiǎn)介(一)
11. Unity開(kāi)啟篇(十一) —— Unity粒子系統(tǒng)簡(jiǎn)介(二)
12. Unity開(kāi)啟篇(十二) —— Unity腳本簡(jiǎn)介(一)

Working with Prefabs

簡(jiǎn)單地跑來(lái)跑去避開(kāi)敵人是一個(gè)非常片面的游戲。 是時(shí)候武裝玩家進(jìn)行戰(zhàn)斗了。

單擊“層次結(jié)構(gòu)”中的Create按鈕,然后選擇3D Object/Capsule。 將其命名為Projectile并為其提供以下變換值:

  • 1) Position: (0, 0, 0)
  • 2) Rotation: (90, 0, 0)
  • 3) Scale: (0.075, 0.246, 0.075)

每次玩家射擊時(shí),它都會(huì)發(fā)射一枚Projectile。 要實(shí)現(xiàn)這一點(diǎn),您需要?jiǎng)?chuàng)建一個(gè)預(yù)制件(Prefab)。 與場(chǎng)景中已有的對(duì)象不同,預(yù)制件是由游戲邏輯按需創(chuàng)建的。

Assets下創(chuàng)建一個(gè)名為Prefabs的新文件夾。 現(xiàn)在將Projectile對(duì)象拖動(dòng)到此文件夾中。 就是這樣:你有一個(gè)預(yù)制件!

你的預(yù)制件需要一些腳本。 在名為ProjectileScripts文件夾中創(chuàng)建一個(gè)新腳本,并向其添加以下類變量:

public float speed;
public int damage;

Vector3 shootDirection;

就像本教程中到目前為止的任何移動(dòng)物體一樣,這個(gè)也會(huì)有速度和傷害speed and damage變量,因?yàn)樗菓?zhàn)斗邏輯的一部分。 shootDirection向量確定了彈丸(Projectile)的去向。

通過(guò)在類中實(shí)現(xiàn)以下方法使該向量起作用:

// 1
void FixedUpdate () {
  this.transform.Translate(shootDirection * speed, Space.World);
}

// 2
public void FireProjectile(Ray shootRay) {
  this.shootDirection = shootRay.direction;
  this.transform.position = shootRay.origin;
}

// 3
void OnCollisionEnter (Collision col) {
  Enemy enemy = col.collider.gameObject.GetComponent<Enemy>();
  if(enemy) {
    enemy.TakeDamage(damage);
  }
  Destroy(this.gameObject);
}

以下是上述代碼中的內(nèi)容:

  • 1) 彈丸的移動(dòng)方式與此游戲中的其他所有方式不同。 它沒(méi)有目標(biāo),或者隨著時(shí)間推移施加一些力量;相反,它在整個(gè)生命周期中沿預(yù)定方向移動(dòng)。
  • 2) 在這里設(shè)置預(yù)制件的起始位置和方向。 這個(gè)Ray看起來(lái)很神秘,但你很快就會(huì)知道它的計(jì)算方法。
  • 3) 如果射彈與敵人發(fā)生碰撞,它會(huì)調(diào)用TakeDamage()并自行摧毀。

在場(chǎng)景層次結(jié)構(gòu)中,將Projectile腳本附加到Projectile GameObject。 將Speed設(shè)置為0.2并將Damage設(shè)置為1,然后單擊位于Inspector頂部附近的Apply按鈕。 這將應(yīng)用您剛剛對(duì)此預(yù)制件的所有實(shí)例所做的更改。

從場(chǎng)景層次結(jié)構(gòu)中移除投射(Projectile)物體——你不再需要它了。


Firing Projectiles

現(xiàn)在你有一個(gè)預(yù)制件可以移動(dòng)和應(yīng)用傷害,你準(zhǔn)備好開(kāi)始射擊。

Player文件夾中,創(chuàng)建一個(gè)名為PlayerShooting的新腳本,并將其附加到場(chǎng)景中的Player。在類內(nèi)部,聲明以下變量:

public Projectile projectilePrefab;
public LayerMask mask;

第一個(gè)變量將包含對(duì)您之前創(chuàng)建的Projectile Prefab的引用。每次你的玩家發(fā)射射彈時(shí),你都會(huì)從這個(gè)預(yù)制件中創(chuàng)建一個(gè)新實(shí)例。mask變量用于過(guò)濾GameObjects

在你的游戲中有時(shí)候你需要知道對(duì)撞是否存在于特定的方向。為此,Unity可以將不可見(jiàn)光線從某個(gè)點(diǎn)投射到您指定的方向。您可能會(huì)遇到許多與光線相交的游戲?qū)ο螅虼耸褂谜谡挚梢赃^(guò)濾掉任何不需要的對(duì)象。

Raycast非常有用,可用于各種用途。它們通常用于測(cè)試其他玩家是否被射彈擊中,但您也可以使用它們來(lái)測(cè)試鼠標(biāo)指針下方是否有任何幾何形狀。要了解有關(guān)Raycast的更多信息,請(qǐng)?jiān)赨nity站點(diǎn)上查看此Unity live training video

下圖顯示了從立方體到圓錐的光線投射。由于光線上有一個(gè)圖標(biāo)球面罩,它會(huì)忽略該GameObect并報(bào)告錐體上的命中:

現(xiàn)在是時(shí)候發(fā)射自己的光線了。

將以下方法添加到PlayerShooting.cs

void shoot(RaycastHit hit){
  // 1
  var projectile = Instantiate(projectilePrefab).GetComponent<Projectile>();
  // 2
  var pointAboveFloor = hit.point + new Vector3(0, this.transform.position.y, 0);

  // 3
  var direction = pointAboveFloor - transform.position;

  // 4
  var shootRay = new Ray(this.transform.position, direction);
  Debug.DrawRay(shootRay.origin, shootRay.direction * 100.1f, Color.green, 2);

  // 5
  Physics.IgnoreCollision(GetComponent<Collider>(), projectile.GetComponent<Collider>());

  // 6
  projectile.FireProjectile(shootRay);
}

以下是上述代碼的作用:

  • 1) 實(shí)例化彈丸Prefab并獲得其彈丸(Projectile)組件,以便初始化。
  • 2) 這一點(diǎn)總是看起來(lái)像(x,0.5,z)。 X和Z是平面上從鼠標(biāo)點(diǎn)擊位置投射光線的坐標(biāo)。這個(gè)計(jì)算很重要,因?yàn)?code>(Projectile)必須與地面平行 - 否則你將向下射擊,只有業(yè)余射擊者才能射向地面。
  • 3) 計(jì)算從Player GameObjectpointAboveFloor的方向。
  • 4) 創(chuàng)建一條新射線,通過(guò)其原點(diǎn)和方向描述射彈軌跡。
  • 5) 這條線告訴Unity的物理引擎忽略了Player colliderProjectile collider之間的碰撞。否則,在它有機(jī)會(huì)飛行之前,將調(diào)用Projectile腳本中的OnCollisionEnter()
  • 6) 最后,它設(shè)定了射彈的軌跡。

注意:在光線投射非常寶貴時(shí)使用Debug.DrawRay(),因?yàn)樗梢詭椭梢暬饩€的外觀和擊中的內(nèi)容。

使用觸發(fā)邏輯,添加以下方法讓玩家實(shí)際拉動(dòng)觸發(fā)器:

// 1
void raycastOnMouseClick () {
  RaycastHit hit;
  Ray rayToFloor = Camera.main.ScreenPointToRay(Input.mousePosition);
  Debug.DrawRay(rayToFloor.origin, rayToFloor.direction * 100.1f, Color.red, 2);

  if(Physics.Raycast(rayToFloor, out hit, 100.0f, mask, QueryTriggerInteraction.Collide)) {
    shoot(hit);
  }
}

// 2
void Update () {
  bool mouseButtonDown = Input.GetMouseButtonDown(0);
  if(mouseButtonDown) {
    raycastOnMouseClick();  
  }
}

下面細(xì)分:

  • 1) 此方法將光線從相機(jī)投射到鼠標(biāo)單擊的位置。 然后它檢查此光線是否與給定的LayerMask的游戲?qū)ο笙嘟弧?/li>
  • 2) 每次更新時(shí),腳本都會(huì)檢查鼠標(biāo)左鍵是否按下。 如果找到一個(gè),則調(diào)用raycastOnMouseClick()

返回Unity并在Inspector中設(shè)置以下變量:

  • Projectile Prefab:從預(yù)制文件夾中引用Projectile
  • Mask:平面

注意:Unity附帶了有限數(shù)量的預(yù)定義圖層,您可以從中創(chuàng)建蒙版。

您可以通過(guò)單擊GameObjectLayer下拉列表并選擇Add Layer來(lái)創(chuàng)建自己的:

要將圖層指定給GameObject,請(qǐng)從圖層下拉列表中選擇它:

有關(guān)圖層的更多信息,請(qǐng)查看Unity's Layers documentation

運(yùn)行項(xiàng)目并隨意開(kāi)火! 射彈向所需的方向發(fā)射,但似乎有點(diǎn)不對(duì),不是嗎?

如果射彈指向行進(jìn)方向,那將會(huì)更加Cool。 要解決此問(wèn)題,請(qǐng)打開(kāi)Projectile.cs腳本并添加以下方法:

void rotateInShootDirection() {
  Vector3 newRotation = Vector3.RotateTowards(transform.forward, shootDirection, 0.01f, 0.0f);
  transform.rotation = Quaternion.LookRotation(newRotation);
}

注意:RotateTowardsMoveTowards非常相似,但它將向量視為方向而不是位置。 此外,您不需要隨時(shí)間更改旋轉(zhuǎn),因此使用接近零的步驟就足夠了。 Unity中的變換旋轉(zhuǎn)使用四元數(shù)(quaternions)表示,這超出了本教程的范圍。 您在本教程中需要了解的是,在進(jìn)行涉及3D旋轉(zhuǎn)的計(jì)算時(shí),它們優(yōu)于矢量。

有興趣了解有關(guān)四元數(shù)的更多信息以及為什么它們有用嗎? 看看這篇優(yōu)秀的文章:How I learned to Stop Worrying and Love Quaternions

FireProjectile()的末尾,添加對(duì)rotateInShootDirection()的調(diào)用。 FireProjectile()現(xiàn)在應(yīng)該如下所示:

public void FireProjectile(Ray shootRay) {
  this.shootDirection = shootRay.direction;
  this.transform.position = shootRay.origin;
  rotateInShootDirection();
}

再次運(yùn)行游戲并向幾個(gè)不同的方向開(kāi)火;這次射彈將指向他們射擊的方向:

刪除Debug.DrawRay調(diào)用,因?yàn)槟鷮⒉辉傩枰鼈儭?/p>


Generating More Bad Guys

只有一個(gè)敵人并不是非常具有挑戰(zhàn)性。 但是既然你了解了Prefabs,你就可以產(chǎn)生你想要的所有對(duì)手!

為了讓玩家猜測(cè),你可以隨機(jī)化每個(gè)敵人的生命,速度和位置。

創(chuàng)建一個(gè)空的游戲?qū)ο?- GameObject \ Create Empty。 將其命名為EnemyProducer并添加Box Collider組件。 在Inspector中設(shè)置值,如下所示:

  • 1)Position: (0, 0, 0)
  • 2)Box Collider:
    • Is Trigger: true
    • Center: (0, 0.5, 0)
    • Size: (29, 1, 29)

您附加的對(duì)撞定義了競(jìng)技場(chǎng)內(nèi)的特定3D空間。 要查看此內(nèi)容,請(qǐng)?jiān)趯哟谓Y(jié)構(gòu)中選擇Enemy Producer GameObject,然后在Scene視圖中查看:

The green wire outlines represent a collider

您將編寫(xiě)一個(gè)腳本,沿X和Z軸在此空間中選擇一個(gè)隨機(jī)位置,并實(shí)例化一個(gè)敵人預(yù)制件。

創(chuàng)建一個(gè)名為EnemyProducer的新腳本,并將其附加到EnemyProducer GameObject。 在新設(shè)置的類中,添加以下實(shí)例成員:

public bool shouldSpawn;
public Enemy[] enemyPrefabs;
public float[] moveSpeedRange;
public int[] healthRange;

private Bounds spawnArea;
private GameObject player;

第一個(gè)變量啟用和禁用spawning。 該腳本將從enemyPrefabs中挑選一個(gè)隨機(jī)的敵人預(yù)制件并實(shí)例化它。 接下來(lái)的兩個(gè)數(shù)組將指定速度和生命值的最小值和最大值。 生成區(qū)域是您在“場(chǎng)景”視圖中看到的綠色框。 最后,你需要一個(gè)對(duì)玩家的引用并將其作為目標(biāo)傳遞給壞人。

在腳本內(nèi)部,定義以下方法:

public void SpawnEnemies(bool shouldSpawn) {
  if(shouldSpawn) {
    player = GameObject.FindGameObjectWithTag("Player");
  }
  this.shouldSpawn = shouldSpawn;
}

void Start () {
  spawnArea = this.GetComponent<BoxCollider>().bounds;
  SpawnEnemies(shouldSpawn);
  InvokeRepeating("spawnEnemy", 0.5f, 1.0f);
}

SpawnEnemies()獲取帶有標(biāo)記Player的游戲?qū)ο蟮囊茫⒋_定敵人是否應(yīng)該生成。

Start()初始化生成區(qū)域并在游戲開(kāi)始后0.5秒調(diào)度方法的調(diào)用。 它會(huì)每秒重復(fù)調(diào)用一次。 除了充當(dāng)setter方法之外,SpawnEnemies()還獲得帶有標(biāo)記Player的游戲?qū)ο蟮囊谩?/p>

Player游戲?qū)ο笊形礃?biāo)記 - 您現(xiàn)在就可以執(zhí)行此操作。 從Hierarchy中選擇Player對(duì)象,然后在Inspector選項(xiàng)卡中,從Tag下拉菜單中選擇Player

現(xiàn)在,您需要為單個(gè)敵人編寫(xiě)實(shí)際的產(chǎn)卵代碼。

打開(kāi)Enemy script并添加以下方法:

public void Initialize(Transform target, float moveSpeed, int health) {
  this.targetTransform = target;
  this.moveSpeed = moveSpeed;
  this.health = health;
}

這只是作為創(chuàng)建對(duì)象的setter。 接下來(lái):產(chǎn)生你的敵人聯(lián)盟的代碼。 打開(kāi)EnemyProducer.cs并添加以下方法:

Vector3 randomSpawnPosition() {
  float x = Random.Range(spawnArea.min.x, spawnArea.max.x);
  float z = Random.Range(spawnArea.min.z, spawnArea.max.z);
  float y = 0.5f;

  return new Vector3(x, y, z);
}

void spawnEnemy() {
  if(shouldSpawn == false || player == null) {
    return;
  }

  int index = Random.Range(0, enemyPrefabs.Length);
  var newEnemy = Instantiate(enemyPrefabs[index], randomSpawnPosition(), Quaternion.identity) as Enemy;
  newEnemy.Initialize(player.transform, 
      Random.Range(moveSpeedRange[0], moveSpeedRange[1]), 
      Random.Range(healthRange[0], healthRange[1]));
}

spawnEnemy()所做的就是選擇一個(gè)隨機(jī)敵人預(yù)制件,在隨機(jī)位置實(shí)例化它并初始化敵人腳本公共變量。

EnemyProducer.cs幾乎準(zhǔn)備好了!

返回Unity。 通過(guò)將Enemy對(duì)象從Hierarchy拖動(dòng)到Prefabs文件夾來(lái)創(chuàng)建Enemy預(yù)制件。 從場(chǎng)景中移除敵人對(duì)象 - 你不再需要它了。 接下來(lái)設(shè)置Enemy Producer腳本公共變量,如下所示:

  • 1) Should Spawn: True
  • 2) Enemy Prefabs:
    • Size: 1
    • Element 0: Reference the enemy prefab
  • 3) Move Speed Range:
    • Size: 2
    • Element 0: 3
    • Element 1: 8
  • 4) Health Range:
    • Size: 2
    • Element 0: 2
    • Element 1: 6

運(yùn)行游戲并查看它 - 無(wú)窮無(wú)盡的壞人!

好吧,那些立方體看起來(lái)并不十分可怕。 是時(shí)候把事情搞清楚了。

在場(chǎng)景中創(chuàng)建3D CylinderCapsule。 分別將它們命名為Enemy2Enemy3。 正如您之前對(duì)第一個(gè)敵人所做的那樣,將兩個(gè)Rigidbody組件和Enemy script添加到它們中。 選擇Enemy2并在Inspector中更改其配置,如下所示:

  • 1) Scale: (0, 0.5, 0)
  • 2) Rigidbody:
    • Use Gravity: False
    • Freeze Position: Y
    • Freeze Rotation: X, Y, Z
  • 3) Enemy Component:
    • Move Speed: 5
    • Health: 2
    • Damage: 1
    • Target Transform: None

現(xiàn)在對(duì)Enemy3做同樣的事情,但是將它的Scale設(shè)置為0.7:

接下來(lái),將它們變成Prefabs,就像你對(duì)原始敵人一樣,并在Enemy Producer中引用所有這些。 檢查器中的值應(yīng)如下所示:

  • Enemy Prefabs:
    • Size: 3
    • Element 0: Enemy
    • Element 1: Enemy2
    • Element 2: Enemy3

運(yùn)行游戲,你會(huì)看到不同的預(yù)制件在競(jìng)技場(chǎng)內(nèi)產(chǎn)生。

不久之后你就會(huì)意識(shí)到自己是無(wú)敵的! 這很棒,你需要稍微調(diào)整一下比賽場(chǎng)地。


Implementing the Game Controller

既然你有射擊,移動(dòng)和敵人,你將實(shí)現(xiàn)一個(gè)基本的游戲控制器。 一旦Player“死”,它將重新開(kāi)始游戲。 但首先,您必須創(chuàng)建一種機(jī)制來(lái)通知任何感興趣的玩家已達(dá)到0健康狀態(tài)。

打開(kāi)Player script并在類聲明上面添加以下內(nèi)容:

using System;

在類內(nèi)添加以下新的公共事件:

public event Action<Player> onPlayerDeath;

事件是一種C#語(yǔ)言功能,允許您將對(duì)象的更改廣播到任何監(jiān)聽(tīng)者。 要了解如何使用活動(dòng),請(qǐng)查看Unity's live training on events.

編輯collidedWithEnemy()如下:

void collidedWithEnemy(Enemy enemy) {
  enemy.Attack(this);
  if(health <= 0) {
    if(onPlayerDeath != null) {
      onPlayerDeath(this);
    }
  }
}

事件為對(duì)象提供了一種簡(jiǎn)潔的方式來(lái)表示它們之間的狀態(tài)變化。 游戲控制器會(huì)對(duì)上面聲明的事件非常感興趣。 在Scripts文件夾中,創(chuàng)建一個(gè)名為GameController的新腳本。 雙擊該文件進(jìn)行編輯,并添加以下變量:

public EnemyProducer enemyProducer;
public GameObject playerPrefab;

腳本需要對(duì)敵人的生產(chǎn)有一些控制權(quán),因?yàn)橐坏┩婕宜劳觯a(chǎn)生敵人是沒(méi)有意義的。 此外,重新啟動(dòng)游戲意味著您必須重新創(chuàng)建Player,這意味著......這是正確的,它將成為預(yù)制件。

添加以下方法:

void Start () {
  var player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player>();
  player.onPlayerDeath += onPlayerDeath;
}

void onPlayerDeath(Player player) {
  enemyProducer.SpawnEnemies(false);
  Destroy(player.gameObject);

  Invoke("restartGame", 3);
}

Start()中,腳本獲取對(duì)Player腳本的引用,并訂閱您之前創(chuàng)建的事件。 一旦玩家的生命值達(dá)到0,將調(diào)用onPlayerDeath(),停止敵人生產(chǎn),從場(chǎng)景中移除Player對(duì)象并在3秒后調(diào)用restartGame()方法。

最后,添加重啟游戲動(dòng)作的實(shí)現(xiàn):

void restartGame() {
  var enemies = GameObject.FindGameObjectsWithTag("Enemy");
  foreach (var enemy in enemies)
  {
    Destroy(enemy);
  }

  var playerObject = Instantiate(playerPrefab, new Vector3(0, 0.5f, 0), Quaternion.identity) as GameObject;
  var cameraRig = Camera.main.GetComponent<CameraRig>();
  cameraRig.target = playerObject;
  enemyProducer.SpawnEnemies(true);
  playerObject.GetComponent<Player>().onPlayerDeath += onPlayerDeath;
}

在這里你做了一些清理工作:你摧毀場(chǎng)景中的所有敵人并創(chuàng)建一個(gè)新的Player對(duì)象。 然后,您將攝像機(jī)裝備的目標(biāo)重新分配給此實(shí)例,恢復(fù)敵方的產(chǎn)生,并將Game Controller訂閱到玩家死亡事件。

現(xiàn)在返回Unity,打開(kāi)Prefabs文件夾,將所有Enemy預(yù)制件的標(biāo)簽更改為Enemy。 接下來(lái),將Player游戲?qū)ο笸先?code>Prefabs文件夾中,使其成為Prefab。 創(chuàng)建一個(gè)空的游戲?qū)ο螅瑢⑵涿麨?code>GameController并附加剛剛創(chuàng)建的腳本。 在Inspector中連接所有必需的引用。

到現(xiàn)在為止,你對(duì)這種模式非常熟悉。 嘗試自己放置引用,然后根據(jù)下面隱藏的插圖檢查結(jié)果:

Game Controller:

  • Enemy Producer:來(lái)自層次結(jié)構(gòu)的敵人生產(chǎn)者引用
  • Player Prefab:從Prefabs文件夾中引用它

再次運(yùn)行游戲以查看game controller的運(yùn)行情況。

就是這樣 - 你編寫(xiě)了第一個(gè)Unity游戲!恭喜!

到現(xiàn)在為止,你應(yīng)該很好地理解將一個(gè)簡(jiǎn)單的動(dòng)作游戲結(jié)合起來(lái)所需要的東西。制作游戲不是一項(xiàng)簡(jiǎn)單的任務(wù);它絕對(duì)需要大量工作,腳本只是將項(xiàng)目變?yōu)楝F(xiàn)實(shí)所需的元素之一。要增加良好的潤(rùn)色程度,您還需要為游戲添加動(dòng)畫(huà)和UI。

后記

本篇主要講述了Unity腳本簡(jiǎn)介,感興趣的給個(gè)贊或者關(guān)注~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,283評(píng)論 6 530
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 97,947評(píng)論 3 413
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,094評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,485評(píng)論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,268評(píng)論 6 405
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,817評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,906評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,039評(píng)論 0 285
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,551評(píng)論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,502評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,662評(píng)論 1 366
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,188評(píng)論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,907評(píng)論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,304評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,563評(píng)論 1 281
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,255評(píng)論 3 389
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,637評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容