[初学Untiy]Space Shooter Tutorial

开始时间:2016年7月31日21:37:37

参考链接:http://unity3d.com/learn/tutorials/projects/space-shooter-tutorial

The player GameObject

add Rigidbody Component

To detect collision, the physics engine, through the rigid body, needs to know the volume of our objects.We need to know how much space these objects take up in our game to calculate the collisions. We give this information to the rigid body by using a cage that we wrap around our game objects. This cage defines the volume of that object. The cage is called a Collider.

add CapsuleCollider Component

remove it

add MeshCollider Component, select “Convex”

The Mesh Collider component will not participate properly in physics
collisions and will not be visible in the scene view unless we select
“Convex” on the Mesh Collider Component.
There are some limitations when using the Mesh Collider. Non-convex Mesh Colliders are only supported on GameObjects without a rigidbody. If you want to use a Mesh Collider on a rigidbody, it needs to be marked as Convex.

select “Is Trigger”

We simply need our collision to trigger an action.

打卡:2016年8月1日21:25:18

Camera And Lighting

Set Camera
- Our game needs to feel like an upright arcade game. These did not have any perspective. So we will choose orthographic as our projection mode.
- set Size to 10
- We want the player in the origin, so we move the camera to let player at the bottom at the start.

by default in Unity 5, a Skybox is included in thescene. This skybox will influence the ambient light affecting the shipand will be used as the background image by the Main Camera.

Set Light
main light, a fill light, a rim light

We can organize our hierarchy by using empty Game Objects.
And It is important to reset the transform of this empty game object.

打卡:2016年8月2日14:08:19(昨天看的有点少)

Adding a background

add a quad object, rotate, remove mesh collider component.

What is a matte paint?
Glossy and flat (or matte) are typical extreme levels of glossiness of a finish. Glossy**paints** are shiny and reflect most light in the specular (mirror-like) direction, while on**flat paints** most of the light diffuses in a range of angles. The gloss level of **paint**can also affect its apparent colour.

  1. Mesh Filter holds the mesh data: Quad.
  2. The Mesh Renderer renders that mesh using the materials in the Mesh Renderer’s Materials Array.
  3. The renderer is only able to use a Texture if it’s a part of a Material.
    In this case, we did not create a material, we simply dragged the texture on the Quad. It was Unity that created the Material for us.
  4. For our Background, let’s change the Shader. Let’s choose Unlit/Texture for the Shader on the nebula materail. Now our Background is independent of our lighting system and it display the texture exactly as it looks in the original image and it uses no lights at all.

move the background down.

读了Lighting Overview

Moving the Player

add Script named PlayerController.cs to Player GameObject

“camelCase” isn’t PascalCase, but “PascalCase” is.

use Input.GetAxis() to 得到运动的分量 来 设置 rb.velocity 从而使飞船运动。

设置飞船运行的限制区域
学习 Class Boundary 的用法

Mathf - A collection of common math functions.

Mathf.Clamp(float value, float min, float max)
将value限制在 min 和 max 之间

设置 飞船的 tilt,让飞船在左右移动的时候有适当的倾斜。

Quaternion Euler(float x, float y, float z)
Returns a rotation that rotates z degrees around the z axis, x degrees around the x axis, and y degrees around the y axis (in that order).

// PlayerController.cs
using UnityEngine;
using System.Collections;

[System.Serializable]       // to serialise our new class we need to use this, and therefore visible in the Inspector
public class Boundary       // we can use this class in other places in our game.
{
    public float xMin, xMax, zMin, zMax;
}


public class PlayerController : MonoBehaviour 
{
    private Rigidbody rb;
    public float speed;     // 飞船速度
    public float tilt;      // 飞船左右运动时的翻转程度
    public Boundary boundary;

    public GameObject shot;
    public Transform shotSpawn;
    public float fireRate;
    public float nextFire;

    void Start()
    {
        rb = GetComponent<Rigidbody> ();
    }

    // FixedUpdate will be called automatically by Unity just before each fixed physics step.
    // All the code we put inside the FixedUpdate function will be executed once per physics step.
    void FixedUpdate()
    {
        float moveHorizontal = Input.GetAxis ("Horizontal");
        float moveVertical = Input.GetAxis ("Vertical");

        Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);
        rb.velocity = movement * speed;         // the direcion we are travelling and how fast we are travelling

        rb.position = new Vector3
        (
            Mathf.Clamp(rb.position.x, boundary.xMin, boundary.xMax),
            0.0f,
            Mathf.Clamp(rb.position.z, boundary.zMin, boundary.zMax)
        );
        rb.rotation = Quaternion.Euler(0.0f, 0.0f, rb.velocity.x * -tilt);  // ???为什么要取相反数才能保证翻转方向正确
    }
}

Creating shots

Creating shots by assembling artwork, physics components and custom C# code.

  1. Create a Object named Bolt, this will be the parent object for our shot.

We are going to separate the game logic from our visual effect of the shot. This will allow us to easily make new weapons with different visual effects by reusing the parent game object with the logic and replacing the visual effect layer.

  1. Create a Quad VFX to hold the visual effect image. Add the VFX game object as a child of Bolt.

  2. Create a Material fx_bolt_orange for the Quad VFX. Drag the Material on to the Quad.

  3. Look at Quad VFX game object, We Change the shader on the material fx_bolt_orange to Particles - Additive

    With shader particles/additive, black has a value of 0 and will add nothing to the scene. And white has a value of 255 on all channels and will add full white to the scene. All of the other colours will be added on top of the existing background. This will give us a strong, hot laser bolt.

We can also change Shader to mobile/particle/additive.
In general the mobile shader will be more efficient with our game’s resource budget, but in some cases may sacrifice either quality or control. The main control that we will lose by using this mobile shader is the ability to change the tint colour, which we don’t need on our laser bolt.

with our visual effect set up, let’s move on to setting up our logic.也就是说配置 Bolt 对象。
5. Add rigid body component to Bolt game object.
6. Remove collider component of the VFX game object.
7. Add Capsule Collider to Bolt game object. And adjust it’s size and orientation.
8. Click is trigger of this collider to make this collider a trigger collider.
9. Add Mover.cs component to Bolt game object.
10. our player is going to shoot many copies or clones of this shot, so let’s save this game object as a prefab. And we set the speed value in this prefab not the INSTANCE in the scene.
11. delete our working instance of Bolt from the scene.
12. to test the Bolt, as we don’t have any shooting code, we simply drag the copies of the prefab into the hierarchy window while the game is running.

// Mover.cs
using UnityEngine;
using System.Collections;

public class Mover : MonoBehaviour
{
    private Rigidbody rb;
    public float speed;     // control the speed of our laser bolt.

    void Start()
    {
        rb = GetComponent<Rigidbody>();

        // We want the laser bolt to travel along the Z axis towards the oncoming hazards
        // The local Z axis of an object is also known as it's transform.forward.
        rb.velocity = transform.forward * speed;
    }
}

Shooting shots

Writing the code and setting up the scene to shoot shots.

What we need to do is instantiate a copy of clone of this Shot prefab when we hit a button or click a mouse during our gameplay.

  1. create a empty game object named Shot Spawn. We can use this empty game object’s transform as a spawn point in our game. This spawn point should move with our player ship. So let’s drag Shot Spawn on to our player game object and drop it as a child. As it’s a child of the player ship, our Shot Spawn’s position will be relative to the player ship.
  2. drag the Shot Spawn out along it’s Z axis until it’s in front of the ship.
// PlayerController.cs
using UnityEngine;
using System.Collections;

[System.Serializable]       // to serialise our new class we need to use this, and therefore visible in the Inspector
public class Boundary       // we can use this class in other places in our game.
{
    public float xMin, xMax, zMin, zMax;
}


public class PlayerController : MonoBehaviour 
{
    private Rigidbody rb;
    public float speed;     // 飞船速度
    public float tilt;      // 飞船左右运动时的翻转程度
    public Boundary boundary;

    public GameObject shot;
    public Transform shotSpawn;
    public float fireRate;
    public float nextFire;

    void Start()
    {
        rb = GetComponent<Rigidbody> ();
    }

    void Update()
    {
        if (Input.GetButton("Fire1") && Time.time > nextFire)
        {
            nextFire = Time.time + fireRate;
            //GameObject clone = 
            Instantiate(shot, shotSpawn.position, shotSpawn.rotation);
        }
    }

    // FixedUpdate will be called automatically by Unity just before each fixed physics step.
    // All the code we put inside the FixedUpdate function will be executed once per physics step.
    void FixedUpdate()
    {
        float moveHorizontal = Input.GetAxis ("Horizontal");
        float moveVertical = Input.GetAxis ("Vertical");

        Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);
        rb.velocity = movement * speed;         // the direcion we are travelling and how fast we are travelling

        rb.position = new Vector3
        (
            Mathf.Clamp(rb.position.x, boundary.xMin, boundary.xMax),
            0.0f,
            Mathf.Clamp(rb.position.z, boundary.zMin, boundary.zMax)
        );
        rb.rotation = Quaternion.Euler(0.0f, 0.0f, rb.velocity.x * -tilt);  // ???为什么要取相反数才能保证翻转方向正确
    }
}

打卡:2016年8月3日20:29:59

Boundary

Creating a bounding box to destroy any object that leaves the game area.

We are going to create a box around our game and we will destroy these shots as they leave the box.(所以我们使用 OnTriggerExit(Collider))

注:The number of units from the top of the screen to the bottom is always twice the value of our camera’s orthographic size.

// DestroyByBundary.cs
using UnityEngine;
using System.Collections;

public class DestroyByBoundary : MonoBehaviour
{
    void OnTriggerExit(Collider other)
    {
        // Destroy everything that leaves the trigger
        Destroy(other.gameObject);
    }
}

Creating hazards

Create an asteroid hazard to challenge the player.

注:We will have a parent game object for the logic and the artwork will be a child.

让小行星随机旋转用到 Random.insideUnitSphere
Returns a random point inside a sphere with radius 1 (Read Only).

Set Angular Drag value to 0. 避免小行星停止自转。

// RandomRotate.cs
using UnityEngine;
using System.Collections;

public class RandomRotater : MonoBehaviour
{
    private Rigidbody rb;
    public float tumble;    // hold our maximum tumble value

    void Start()
    {
        rb = GetComponent<Rigidbody>();
        rb.angularVelocity = Random.insideUnitCircle * tumble;  // 给小行星一个随机的角速度,让它旋转
    }

Write trigger collider code for our 2 colliders to have any effect.

我们发现小行星由于和Boundary接触而消失,为了避免这一效果,tag our boundary.

// DestroyByContact.cs
using UnityEngine;
using System.Collections;

public class DestroyByContact : MonoBehaviour
{ 
    void OnTriggerEnter(Collider other)
    {
        // Debug.Log(other.name);
        if(other.tag == "Boundary") // 当 other 的 tag 是 Boundary 的时候,跳过
        {
            return;
        }
        Destroy(other.gameObject);  // Destroy the laser bolt when it hits the asteroid.
        Destroy(gameObject);        // Destroy the asteroid itself.
    }
}

打卡:2016年8月5日19:40:31

Explosions

Add explosions to the scene when hazards or the player is destroyed.

  1. Add explosions to the scene when hazards or the player is destroyed.两个爆炸效果。
  2. Get the asteroid moving towards the player. Use Mover.cs created before, and set the speed a negative number to set it’s direction right. 很方便的是,当小行星碰撞Boundary的时候也会被销毁。从而不需要再配置另一Destroy程序了。
  3. 将Asteroid GameObject 保存为Prefab.
// DestroyByContact.cs
using UnityEngine;
using System.Collections;

public class DestroyByContact : MonoBehaviour
{
    public GameObject explosion;        
    public GameObject playerExplosion;

    void OnTriggerEnter(Collider other)
    {
        // Debug.Log(other.name);
        if(other.tag == "Boundary") // 当 other 的 tag 是 Boundary 的时候,跳过
        {
            return;
        }
        Instantiate(explosion, transform.position, transform.rotation);
        if (other.tag == "Player")  // 如果是飞船和小行星碰撞的话,要生成飞船的explosion
        {
            Instantiate(playerExplosion, other.transform.position, other.transform.rotation);
        }
        Destroy(other.gameObject);  // Destroy the laser bolt when it hits the asteroid.
        Destroy(gameObject);        // Destroy the asteroid itself.
    }
}

疑问:爆炸效果 GameObject 并没有被销毁。

Game Controller

Write a controller to spawn our hazards, keep track of and display our score, end the game and allow the game to be restarted.

  1. Create a new GameObject “GameController” to hold our game controller logic. (This GameObject will not have a physical presence in our game. ) And tag it with GameController.
// GameController.cs
using UnityEngine;
using System.Collections;

public class GameController : MonoBehaviour
{
    public GameObject hazard;
    public Vector3 spawnValues;

    void Start()
    {
        SpawnWaves();
    }

    void SpawnWaves()
    {
        Vector3 spawnPosition = new Vector3(Random.Range(-spawnValues.x, spawnValues.x), spawnValues.y, spawnValues.z);
        Quaternion spawnRotation = Quaternion.identity;
        Instantiate(hazard, spawnPosition, spawnRotation);
    }
}

Spawning Waves

Spawn infinite waves of hazards to change our player.

  1. To have a function that can pause without pausing our entire game, we need to make this function a coroutine.
  2. 为了Destroy explosion,使用另一种销毁方法,destroy things by time.
using UnityEngine;
using System.Collections;

public class GameController : MonoBehaviour
{
    public GameObject hazard;   
    public Vector3 spawnValues;     // 通过Inspector来设置spawnValues,继而在SpawnWaves中设置spawnPosition
    public int hazardCount;         // 每一波的小行星数量
    public float spawnWait;         // 相邻两个小行星的生成时间间隔
    public float startWait;         // a short pause after the game starts for the player to get ready
    public float waveWait;          // 相邻两波之间的时间间隔

    void Start()
    {
        StartCoroutine(SpawnWaves());
    }

    IEnumerator SpawnWaves()
    {
        yield return new WaitForSeconds(startWait);
        while(true)
        {
            for (int i = 0; i < hazardCount; i++)
            {
                Vector3 spawnPosition = new Vector3(Random.Range(-spawnValues.x, spawnValues.x), spawnValues.y, spawnValues.z);
                Quaternion spawnRotation = Quaternion.identity;
                Instantiate(hazard, spawnPosition, spawnRotation);
                yield return new WaitForSeconds(spawnWait);
            }
            yield return new WaitForSeconds(waveWait);
        }
    }
}
// DestroyByTime.cs
using UnityEngine;
using System.Collections;

public class DestroyByTime : MonoBehaviour
{
    public float lifeTime;

    void Start()
    {
        Destroy(gameObject, lifeTime);
    }
}

Part 3 Scoring,Finishing And Building the Game

Audio

Add audio effects for the weapons, explosions and a background music track.

We associate an audio clip with a game object by using an audio source component. The audio source plays an audio clip.
We can add an audio source component to our game objects and then reference an audio clip for that source to play.
But ther is an easier way to do this, If we drag an audio clip on to a game object , Unity will create a new audio source on the game object and automatically reference the dragged audio clip.

  1. drag audio explosion_asteroid to explosion_asteroid, and click “Play On Awake”.这样当实例化这个Game Object的时候,就会自动播放这个音频。
  2. 在设置背景音乐的时候 click “Loop”.
  3. Balancing the audio.调整音量。调低背景音和子弹音量。
using UnityEngine;
using System.Collections;

[System.Serializable]       // to serialise our new class we need to use this, and therefore visible in the Inspector
public class Boundary       // we can use this class in other places in our game.
{
    public float xMin, xMax, zMin, zMax;
}


public class PlayerController : MonoBehaviour 
{
    private Rigidbody rb;
    private AudioSource audioSource;

    public float speed;     // 飞船速度
    public float tilt;      // 飞船左右运动时的翻转程度
    public Boundary boundary;

    public GameObject shot;
    public Transform shotSpawn;
    public float fireRate;
    public float nextFire;

    void Start()
    {
        rb = GetComponent<Rigidbody> ();
        audioSource = GetComponent<AudioSource>();
    }

    void Update()
    {
        if (Input.GetButton("Fire1") && Time.time > nextFire)
        {
            nextFire = Time.time + fireRate;
            //GameObject clone = 
            Instantiate(shot, shotSpawn.position, shotSpawn.rotation);
            audioSource.Play();
        }
    }

    // FixedUpdate will be called automatically by Unity just before each fixed physics step.
    // All the code we put inside the FixedUpdate function will be executed once per physics step.
    void FixedUpdate()
    {
        float moveHorizontal = Input.GetAxis ("Horizontal");
        float moveVertical = Input.GetAxis ("Vertical");

        Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);
        rb.velocity = movement * speed;         // the direcion we are travelling and how fast we are travelling

        rb.position = new Vector3
        (
            Mathf.Clamp(rb.position.x, boundary.xMin, boundary.xMax),
            0.0f,
            Mathf.Clamp(rb.position.z, boundary.zMin, boundary.zMax)
        );
        rb.rotation = Quaternion.Euler(0.0f, 0.0f, rb.velocity.x * -tilt);  // ???为什么要取相反数才能保证翻转方向正确
    }
}

Counting Points and Displaying the Score(NICE)

Counting points and displaying the score.

  1. Creating a display for the score before we create a score value to feed it with. Adding a GUIText Component.
  2. Update the GUIText’s transform position to (0.5, 0.5, 0.0)- This will place the GUIText into the middle of the screen.
  3. Update the GUIText.text with “Score Text”.- This will make the GUIText object easier to see on screen.

注:GUI text is drawn on a layer above our game, not in the game world. We won’t see it in our Scene view. GUI text uses something called Viewport Space, rather than screen space. Screen space is defined in pixels, Viewport Space is defined simply as a single width and height value with (0, 0) in the lower left, and (1, 1) in the upper right.

  1. Move Score Text to upper left of. Give some pad around it to separate it from the edges of the game screen Use pixel offset
  2. Feed Score Text a score value.
    注:不能再运行前用Hierarchy 中的 GameObject 初始化 Prefab 中的 内容。
// GameController.cs
using UnityEngine;
using System.Collections;

public class GameController : MonoBehaviour
{
    public GameObject hazard;   
    public Vector3 spawnValues;     // 通过Inspector来设置spawnValues,继而在SpawnWaves中设置spawnPosition
    public int hazardCount;         // 每一波的小行星数量
    public float spawnWait;         // 相邻两个小行星的生成时间间隔
    public float startWait;         // a short pause after the game starts for the player to get ready
    public float waveWait;          // 相邻两波之间的时间间隔

    public GUIText scoreText;
    private int score;

    void Start()
    {
        score = 0;      // starting value
        UpdateScore();
        StartCoroutine(SpawnWaves());
    }

    IEnumerator SpawnWaves()
    {
        yield return new WaitForSeconds(startWait);
        while(true)
        {
            for (int i = 0; i < hazardCount; i++)
            {
                Vector3 spawnPosition = new Vector3(Random.Range(-spawnValues.x, spawnValues.x), spawnValues.y, spawnValues.z);
                Quaternion spawnRotation = Quaternion.identity;
                Instantiate(hazard, spawnPosition, spawnRotation);
                yield return new WaitForSeconds(spawnWait);
            }
            yield return new WaitForSeconds(waveWait);
        }
    }

    public void AddScore(int scoreValue)
    {
        score += scoreValue;
        UpdateScore();
    }

    void UpdateScore()
    {
        scoreText.text = "Score: " + score;
    }
}
// DestroyByContact.cs
using UnityEngine;
using System.Collections;

public class DestroyByContact : MonoBehaviour
{
    public GameObject explosion;        
    public GameObject playerExplosion;
    public int scoreValue;                  // 每击中一个的分数
    private GameController gameController;  // 在运行时通过脚本设置,不能直接将 GameController GameObject 拖入 Asteroid Prefab。

    void Start()
    {
        GameObject gameControllerObject = GameObject.FindWithTag("GameController"); // 读取GameObject GameController
        if(gameControllerObject != null)
        {
            gameController = gameControllerObject.GetComponent<GameController>();   // 读取Component GameController(就是那个脚本文件)
        }
        if(gameController == null)
        {
            Debug.Log("Cannot find 'GameController' script");
        }
    }

    void OnTriggerEnter(Collider other)
    {
        // Debug.Log(other.name);
        if(other.tag == "Boundary") // 当 other 的 tag 是 Boundary 的时候,跳过
        {
            return;
        }
        Instantiate(explosion, transform.position, transform.rotation);
        if (other.tag == "Player")  // 如果是飞船和小行星碰撞的话,要生成飞船的explosion
        {
            Instantiate(playerExplosion, other.transform.position, other.transform.rotation);
        }
        gameController.AddScore(scoreValue);
        Destroy(other.gameObject);  // Destroy the laser bolt when it hits the asteroid.
        Destroy(gameObject);        // Destroy the asteroid itself.
    }
}

Ending the Game

End the game and show the state of the game with labels for Game Over and Restart.

  1. To keep our hierarchy organized, let’s create an empty parent game object to hold our current score text and two new labels(Restart Text, Game Over Text) we are about to create. Rename the game object Display Text.

  2. Update our code on our gamecontroller script. To capture a keypress, let’s pool for it in update.

using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections;

public class GameController : MonoBehaviour
{
    public GameObject hazard;   
    public Vector3 spawnValues;     // 通过Inspector来设置spawnValues,继而在SpawnWaves中设置spawnPosition
    public int hazardCount;         // 每一波的小行星数量
    public float spawnWait;         // 相邻两个小行星的生成时间间隔
    public float startWait;         // a short pause after the game starts for the player to get ready
    public float waveWait;          // 相邻两波之间的时间间隔

    public GUIText scoreText;       // We will feed information to these labels as the game progresses.
    public GUIText restartText;
    public GUIText gameOverText;

    private bool gameOver;
    private bool restart;
    private int score;

    void Start()
    {
        gameOver = false;
        restart = false;
        gameOverText.text = "";
        restartText.text = "";
        score = 0;      // starting value
        UpdateScore();
        StartCoroutine(SpawnWaves());
    }

    void Update()
    {
        if(restart)
        {
            if(Input.GetKeyDown(KeyCode.R))
            {
            //    Application.LoadLevel(Application.loadedLevel);     // restart the game
                SceneManager.LoadScene("_Scenes/main");         // 重新加载这个 Scene     Loads the scene by its name or index in Build Settings.
            }
        }
    }

    IEnumerator SpawnWaves()
    {
        yield return new WaitForSeconds(startWait);
        while(true)
        {
            for (int i = 0; i < hazardCount; i++)
            {
                Vector3 spawnPosition = new Vector3(Random.Range(-spawnValues.x, spawnValues.x), spawnValues.y, spawnValues.z);
                Quaternion spawnRotation = Quaternion.identity;
                Instantiate(hazard, spawnPosition, spawnRotation);
                yield return new WaitForSeconds(spawnWait);
            }
            yield return new WaitForSeconds(waveWait);
            if(gameOver)            // 游戏结束的时候,询问是否重来
            {
                restartText.text = "Press 'R' for Restart";
                restart = true;     
                break;              // 结束这个 spawnwaves 循环
            }
        }
    }

    public void AddScore(int scoreValue)
    {
        score += scoreValue;
        UpdateScore();
    }   

    void UpdateScore()
    {
        scoreText.text = "Score: " + score;
    }

    public void GameOver()      // 如同 AddScore 一样,此函数可以由别的GameObject调用。
    {
        gameOverText.text = "Game Over!";
        gameOver = true;
    }
}
using UnityEngine;
using System.Collections;

public class DestroyByContact : MonoBehaviour
{
    public GameObject explosion;        
    public GameObject playerExplosion;
    public int scoreValue;                  // 每击中一个的分数
    private GameController gameController;  // 在运行时通过脚本设置,不能直接将 GameController GameObject 拖入 Aeteroid Prefab。
                                            //  a variable holding the reference to our game controller instance.

    void Start()
    {
        GameObject gameControllerObject = GameObject.FindWithTag("GameController"); // 读取GameObject GameController
        if(gameControllerObject != null)
        {
            gameController = gameControllerObject.GetComponent<GameController>();   // 读取Component GameController(就是那个脚本文件)
        }
        if(gameController == null)
        {
            Debug.Log("Cannot find 'GameController' script");
        }
    }

    void OnTriggerEnter(Collider other)
    {
        // Debug.Log(other.name);
        if(other.tag == "Boundary") // 当 other 的 tag 是 Boundary 的时候,跳过
        {
            return;
        }
        Instantiate(explosion, transform.position, transform.rotation);
        if (other.tag == "Player")  // 如果是飞船和小行星碰撞的话,要生成飞船的explosion
        {
            Instantiate(playerExplosion, other.transform.position, other.transform.rotation);
            gameController.GameOver();  // 告诉 GameController game is over
        }
        gameController.AddScore(scoreValue);
        Destroy(other.gameObject);  // Destroy the laser bolt when it hits the asteroid.
        Destroy(gameObject);        // Destroy the asteroid itself.
    }
}

Building the Game

Build the completed game and deploy it to the web.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值