Unity学习记录

前言

unity自学笔记

参考教程

Unity2023新手入门教程_超细节100集课程_哔哩哔哩_bilibili

project1

项目界面

第一个脚本

Debug.Log的使用

public class SL00 : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()                                           // Debug的使用,在console里返回参数
    {
        Debug.Log("** 开始测试 . .");

        GameObject obj = this.gameObject;
        string name = obj.name;
        Debug.Log("** 物体名字:" + this.name);            // this.name其实指向this.gameObject.name简化了gameObject(this其实也能省)

        Transform tr = this.gameObject.transform;          // localposition获取本地坐标
        Vector3 pos = this.gameObject.transform.position;  // 查看可知position的数据类型
        Debug.Log("** 物体的位置:" + pos.ToString("F3")); // 将数值类型(通常是浮点数)转换为字符串,并指定格式化字符串。在这里,"F3"是格式化字符串
    }
}

 运行后:

第二个脚本

获取和修改物体位置数据

public class SL01 : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()                                            // 获取和修改物体位置数据
    {
        Debug.Log("** 开始测试:");

        Transform tr1 = this.gameObject.transform;          // localPosition获取本地坐标
        Vector3 pos1 = this.gameObject.transform.localPosition;  // 查看可知position的数据类型
        Debug.Log("** 物体的局部位置:" + pos1.ToString("F3"));

        this.transform.localPosition = new Vector3(1.5f, 2.5f, 0);

    }
}

注意到Transform组件的参数都是local,相对于父物体的。

同时还发现unity先执行了SL01修改了位置,再执行SL00

第三个脚本

小车实现匀速直行,按shift加F键可以锁定视角

public class SL02 : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()                                          // 对应10.1帧更新,用Update获取动态的参数;以及10.2的移动物体
    {
        Application.targetFrameRate = 60;                 // 不支持固定帧率,但可以设定一个近似帧率
    }

    // Update is called once per frame
    void Update()                                       // 游戏引擎每更新一帧都会调用Update方法
    {
        Debug.Log("** 帧更新 Update:Time = " + Time.time);  // 获取游戏时间(如果是Time.deltaTime获取的是上次更新时间差)

        float speed = 50;
        float distance = speed * Time.deltaTime;             // 两帧间匀速小车移动的距离

        Vector3 pos = this.transform.localPosition;          // 直接 + 0.01f 实现物体移动,但非匀速,还会导致FPS越高速度越快
        pos.z += distance; // 0.01f;                         
        this.transform.localPosition = pos;

    }
}

project2

项目界面

第一个脚本

public class SL00 : MonoBehaviour
{
    GameObject flag;
    // Start is called before the first frame update
    void Start()
    {
        
        flag = GameObject.Find("红旗");              // 获取坐标
        this.transform.LookAt(flag.transform);       // 使z轴指向目标

        Vector3 p1 = this.transform.position;
        Vector3 p2 = flag.transform.position;
        //Vector3 p = p2 - p1;
        //float distance = p.magnitude;                // 计算两个坐标间的距离
        float distance = Vector3.Distance(p2, p1);     // 等价于上面两行

        Debug.Log("** 两个物体之间的距离:" + distance);

    }

    // Update is called once per frame
    void Update()
    {

        Vector3 p1 = this.transform.position;
        Vector3 p2 = flag.transform.position;
        Vector3 p = p2 - p1;
        float distance = p.magnitude;

        if ( distance > 0.3f )                       // 做停止判断
        {
            float speed = 2;
            float move = speed * Time.deltaTime;

            this.transform.Translate(0, 0, move, Space.Self);   // Translate(dx, dy, dz, 世界或自身坐标系)

        }
    }
}

第二个脚本

public class SL01 : MonoBehaviour
{
    float rotateSpeed = 30;
    // Start is called before the first frame update
    void Start()
    {
        Application.targetFrameRate = 60;

        // transform.rotation   官方不建议使用
        transform.localEulerAngles = new Vector3(0, 45, 0);
    }

    // Update is called once per frame
    void Update()
    {
        Vector3 angles = this.transform.localEulerAngles;
        angles.y += rotateSpeed * Time.deltaTime;
        this.transform.localEulerAngles = angles;
        // 等价于this.transform.Rotate(0, rotateSpeed * Time.deltaTime, 0, Space.self);
    }
}

第三个脚本

挂在地球上的实现地球自转

public class SL02 : MonoBehaviour
{
    float rotateSpeed = 30;
    void Update()
    {
        this.transform.Rotate(0, rotateSpeed * Time.deltaTime, 0, Space.Self);
    }
}

 第四个脚本

挂在卫星上实现卫星绕地球旋转

public class SL03 : MonoBehaviour
{
    float RotateSpeed = 60;
    void Update()
    {
        Transform parent = this.transform.parent; // 如果脚本附加到卫星上,this.transform.parent将指向卫星的上一级卫星系统的Transform组件(B是A的子级)
        parent.Rotate(0, RotateSpeed * Time.deltaTime, 0, Space.Self);

    }
}

角度问题

注意到角度问题,先说结论:调节任意物体Inspector界面的rotation的x坐标和z坐标都是绕自身(也就是物体坐标系)旋转,当调整y坐标时是绕父物体的物体坐标系的y轴旋转。

但是在例如Rotate函数里的Space.Self和Space.World就是绕物体坐标系和绕惯性坐标系(包括y轴)。

另外,如果是采用

// localEulerAngles
        Vector3 angle = transform.localEulerAngles;
        Debug.Log(angle);
        angle.y += 1;
        transform.localEulerAngles = angle;
// eulerAngles
        Vector3 angle = transform.eulerAngles;
        Debug.Log(angle);
        angle.y += 1;
        transform.eulerAngles = angle;

进行调节,就会发现修改localEulerAngles等价于修改Inspector界面上的Rotation。 

先在卫星上调节rotation.z,发现卫星绕自身z轴旋转

再在卫星上调节rotation.y

说明调节卫星的rotation.y不是绕自身的Local(物体坐标系)的y轴旋转,而是绕他的父物体卫星系统的Local(物体坐标系)来旋转的

使用上面的代码的LocalEulerAngles运行后会有个直观的结果

可以看到子物体和父物体的z轴方向也不一样,所以我们试试绕z转 

        Vector3 angle = transform.localEulerAngles;
        Debug.Log(angle);
        angle.z += 1;
        transform.localEulerAngles = angle;

 

 显然发现地球是绕自身z轴旋转而非父物体z轴

但是,这里在使用该代码实现绕x轴的时候会出现LocalEulerAngles的x坐标卡在89点多过不去,这里应该是万向锁的问题导致

采用四元数来避免万向锁问题 

        // 获取当前的旋转四元数
        Quaternion currentRotation = transform.rotation;
        // 计算旋转增量
        float rotationAmount = rotateSpeed * Time.deltaTime;

        // 创建一个围绕物体局部坐标系Y轴旋转的四元数
        Quaternion deltaRotation = Quaternion.Euler(rotationAmount, 0, 0);

        // 应用旋转增量
        transform.rotation = currentRotation * deltaRotation;

 万向锁介绍

无伤理解欧拉角中的“万向死锁”现象_哔哩哔哩_bilibili

project3

项目界面

第一个脚本

public class SL00 : MonoBehaviour
{
    // 执行顺序
    // 依次先执行每个脚本组件的awake方法,第一阶段初始化完毕
    // 再把每一个脚本组件的Start方法执行一边,第二阶段初始化完毕        执行脚本的组件顺序随机
    // 帧更新......
    
    private void Awake() // 消息函数 Start只执行一次,Awake比Start先调用,同时在组件禁用的情况下Awake仍调用但start不会
    {
        Debug.Log("** Awake(), 初始化:");
    }
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("** Start(), 初始化:");
    }
    // Update is called once per frame
    void Update()
    {   
    }
    private void OnEnable()                      // 比Start快比Awake慢,在启用组件时会执行一次
    {
        Debug.Log("** OnEnable(), 初始化:");
    }
    private void OnDisable()                    // 在禁用组件时会执行一次
    {
        Debug.Log("** OnDisable(), 初始化:");
    }
}

注意到第一行的Debug.Log来自MainLogic脚本的Awake(),虽然该脚本处于禁用状态,但仍会执行里面的Awake()方法

第二个脚本

public class SL01 : MonoBehaviour
{
    [Tooltip("y轴方向角速度")]      // 为public变量添加注释          
    public float rotateSpeed = 60;
    void Update()
    {
        this.transform.Rotate(0, rotateSpeed * Time.deltaTime, 0, Space.Self);
    }
}

 第三个脚本

重复内容

第四个脚本

该脚本挂在风扇(1)上,实现按住鼠标左键就旋转,松开停止

public class SL03 : MonoBehaviour
{
    float speed = 0;

    void Update()
    {

        if ( Input.GetMouseButtonDown( 0 ) )             // 鼠标事件,按下执行一次,0左键,1右键,2滚轮
        {
            Debug.Log("** 鼠标按下");
            speed = 180;
        }

        if ( Input.GetMouseButtonUp( 0 ) )
        {
            Debug.Log("** 鼠标抬起");
            speed = 0;
        }

        this.transform.Rotate(0, speed * Time.deltaTime, 0, Space.Self);

        if ( Input.GetMouseButton(0) )                  // 鼠标状态,持续返回bool值
        {
            Debug.Log("** 鼠标按下ing");
        }
        else
        {
            Debug.Log("** 鼠标抬起ing");
        }

    }
}

第五个脚本

该脚本挂在小火车(1)上,实现该物体移动超出屏幕范围后自动停止

public class SL04 : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Vector3 pos = this.transform.position;
        Vector3 screenPos = Camera.main.WorldToScreenPoint(pos);         // 世界坐标转为屏幕坐标
        Debug.Log("** " + this.name + "屏幕位置为:" + pos);
    }
    // Update is called once per frame
    void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {
            Vector3 mousePos = Input.mousePosition;                     // 获取鼠标屏幕坐标
            Debug.Log("** 鼠标位置为:" + mousePos);
        }

        Vector3 pos = this.transform.position;
        Vector3 screenPos = Camera.main.WorldToScreenPoint(pos);

        if (screenPos.y < 0 || screenPos.y > Screen.height)
        {
            Debug.Log("** " + this.name + "超出屏幕范围");
        }
        else
        {
            transform.Translate(0, 0, 2 * Time.deltaTime, Space.Self);
        }
    }
}

第六个脚本

重复内容

第七个脚本

通过脚本操控其他组件,该脚本挂在游戏主控上

public class SL06 : MonoBehaviour
{
    // Update is called once per frame
    void Update()           // 获取组件,用代码改变组件参数
    {
        if (Input.GetMouseButtonDown(0))
        {
            PlayMusic();
        }
    }

    void PlayMusic()         // play on Awake按键游戏开始时自动播放音乐
    {
        AudioSource audio = this.GetComponent<AudioSource>(); // 获取组件
        audio.mute = false;     // 通过代码修改组件参数,mute 被设置为 true,则该音频源将被静音,不会播放声音
        if(audio.isPlaying)
        {
            Debug.Log("* 停止播放");
            audio.Stop();
        }
        else
        {
            Debug.Log("* 开始播放音乐");
            audio.Play();
        }
    }
}

project4

项目界面

第一个脚本

该脚本实现父类子类的寻找

public class SL00 : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Transform parent = transform.parent;    // parent的类型是Transform
        Debug.Log("** 父级:" + parent.name);

        foreach (Transform a in this.transform.parent)  // 在transform组件下遍历出来的就是他的子节点的transform组件
        {                                               // 且可以发现transform前面的this也可以省
            Debug.Log("** " + parent.name + "的子物体:" + a.name);
        }
        // 按索引获取子节点
        Transform child = parent.transform.GetChild(0);
        Debug.Log("** " + parent.name + "的第一个子物体:" + child.name);
        // 按名称获取子节点
        Transform child1 = parent.transform.Find("bb"); // 注意Find只能找子物体,找不到bbcc,需指定写成bb/bbcc,表示从bb下查找子物体
        Debug.Log("** " + parent.name + "的第二个子物体:" + child1.name);  
    }
}

第二个脚本

public class SL01 : MonoBehaviour
{
    void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {
            Test();
        }
    }
    void Test()
    {
        Transform parentNode = transform.Find("/111");      // 在根目录下面找其子级111
        transform.SetParent(parentNode);
        // transform.SetParent(null); 此代码可以将null设为父级,这样他就不是任何节点的子级

        Transform item = transform.Find("aa");
        if (item.gameObject.activeSelf)       // 判断物体是否显示,该属性是gameObject下的
        {
            item.gameObject.SetActive(false); // 如果显示就是设置为不显示
        }
        else
        {
            item.gameObject.SetActive(true);
        }
    }
}

第三个脚本

获取别的物体的组件,脚本,并修改参数

public class MainLogic : MonoBehaviour
{
    public AudioSource bgm;    // 这个比bgmNode更直接一些,直接获取组件,GameObject是先获取节点,再用GetComponent获取该节点的组件
    public GameObject bgmNode;

    public GameObject fanNode;
    void Start()                                       // 获取别的节点下的组件
    {
        Application.targetFrameRate = 60;
        bgm.Play();
        // 等价于
        // AudioSource audio = bgmNode.GetComponent<AudioSource>();           用GetComponent获取该节点下的XX组件
        // audio.Play();
    }

    void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {
            DoWork();
        }
    }

    void DoWork()         // void前面什么都不加默认为private void,表示只有内部可调用的方法,public void表示可在外部调用
    {
        FanLogic fan = fanNode.GetComponent<FanLogic>();
        fan.rotateSpeed = 180;
    }
}

其中FanLogic为

public class FanLogic : MonoBehaviour
{
    public float rotateSpeed;

    void Update()
    {
        this.transform.Rotate(0, rotateSpeed * Time.deltaTime, 0, Space.Self);
    }
}

 第四个脚本

该脚本实现一个简易的俄罗斯方块的下落效果

当按下空格键后调用ChangeShape()实现方块的切换,同时设置isMoving为True启动TransForm(),使得方块向下移动,当到地面时停止下降并更改isMoving1禁用ChangeShape()

public class playerLogic : MonoBehaviour
{
    int index = 0;
    float speed = 0;
    bool isMoving;
    bool isMoving1 = true;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            isMoving = true;
            if(isMoving1)
                ChangeShape();
        }

        if (isMoving)
        {
            TransForm();
        }
    }

    void ChangeShape()
    {
        Transform child = transform.GetChild(index);
        child.gameObject.SetActive(false);
        index += 1;
        if (index >= transform.childCount)
            index = 0;
        child = transform.GetChild(index);
        child.gameObject.SetActive(true);
    }

    void TransForm()
    {
        Vector3 pos = this.transform.position;
        if (pos.y >= 0)
        {
            speed = 3;
            transform.Translate(0, -speed * Time.deltaTime, 0, Space.World);
        }
        else
        {
            isMoving1 = false;
        }
    }
}

project5

项目界面

汽车操控

控制汽车移动

public class moveLogic : MonoBehaviour
{
    [Tooltip("指定速度向量")]
    public Vector3 speed;
    public float moveSpeed = 10.0f;
    public float rotationSpeed = 100.0f;
    // Start is called before the first frame update

    void Update()
    {
        // transform.Translate(speed * Time.deltaTime, Space.World);
        float horizontalInput = Input.GetAxis("Horizontal"); // 获取水平输入
        float verticalInput = Input.GetAxis("Vertical"); // 获取垂直输入

        // 根据输入控制汽车的转向和移动
        transform.Rotate(0, horizontalInput * rotationSpeed * Time.deltaTime, 0);
        transform.Translate(0, 0, verticalInput * moveSpeed * Time.deltaTime);
    }
}

控制镜头跟随

public class cameraLogic : MonoBehaviour
{
    public Transform target; // 汽车的Transform组件
    public Vector3 offset = new Vector3(0, 1, 0); // 相机偏移,根据需要调整

    void LateUpdate()
    {
        // 设置摄像机位置为汽车位置加上偏移
        transform.position = target.position + offset;
        transform.rotation = target.rotation;
    }
}

LateUpdate() 函数会在每一帧的最后被调用。这使得它非常适合处理需要在其他对象的 Update() 函数执行后执行的逻辑。通常,LateUpdate() 用于处理与游戏对象之间的相互作用,例如相机跟随、角色控制等,以确保这些逻辑不会受到其他对象 Update() 函数的影响。如果你有两个脚本JS1、JS2,两个脚本中都有Update()函数, 在JS1中有 lateUpdate ,JS2中没有。那么 lateUpdate 函数会等待JS1、JS2两个脚本的Update()函数 都执行完后才执行。

定时调用

Invoke(func, delay),只调用一次

IvokeRepeating(func, delay, interval),循环调用 (在延时delay秒后开始,然后每隔interval秒调用func函数)

IsInvoking(func),是否正在调度

CancelInvoke(func),取消调用,从调用队列中移除

以下脚本挂在Sphere上

public class SphereLogic : MonoBehaviour
{
    public float speed = 2;
    // Start is called before the first frame update
    void Start()               // start只执行一次,但invokerepeating会随时间不断执行,(永不结束的方法)
    {
        Application.targetFrameRate = 60;
        Debug.Log("* " + Time.time);
        this.Invoke("DoSomeThing", 2);      // invoke定时调用一次函数,这里定时2秒后执行方法
    }

    // Update is called once per frame
    void Update()
    {
        if(Input.GetMouseButtonDown(1))
        {
/*            if(IsInvoking("Translate"))    // isinvoking判断Translate是否在invoke队列里
            {
                CancelInvoke("Translate"); // 如果有就删掉旧进程,避免重复调用(重复调用效果可能会叠加)
            }
            InvokeRepeating("Translate", 0, Time.deltaTime);  // 实现在点击一次鼠标后持续移动*/
            // 可以这样写
            if (!IsInvoking("Translate"))  // !bool值取反
            {
                InvokeRepeating("Translate", 0, Time.deltaTime);
            }
            if (IsInvoking("Change"))
                CancelInvoke("Change");
            else
                this.InvokeRepeating("Change", 2, 2); // 2秒后开始调用,之后每2秒执行一次
        }
    }

    void Change()
    {
        speed = -speed;
    }

    void DoSomeThing()
    {
        Debug.Log("* " + Time.time);
    }

    void Translate()
    {
        this.transform.Translate(0, speed * Time.deltaTime, 0, Space.Self);
        Debug.Log(Time.time);
    }
}

里面还有一点点下面红绿灯的结果图 

以下脚本挂在风扇上,用循环调用实现风扇旋转的加减速

public class FanLogic : MonoBehaviour
{
    float rotateSpeed = 0;
    public float MaxSpeed = 720;
    bool SpeedUp = false;
    // Start is called before the first frame update
    void Start()
    {
        InvokeRepeating("Acc", 0, 0.2f);
    }

    // Update is called once per frame
    void Update()
    {
        transform.Rotate(0, rotateSpeed * Time.deltaTime, 0, Space.Self);
        if (Input.GetMouseButtonDown(2))
        {
            SpeedUp = !SpeedUp;
        }
    }

    void Acc()
    {
        if (SpeedUp)
        {
            if(rotateSpeed < MaxSpeed)
                rotateSpeed += 20;
        }
        else
        {
            if (rotateSpeed > 0)
                rotateSpeed -= 20;
            else
                rotateSpeed = 0;
        }
    }
}

资源数组

就是数组的使用,该脚本挂在cube下的音乐盒上

public class musicBox : MonoBehaviour
{
    public AudioClip[] audioClips;                    // 创建数组
    // Start is called before the first frame update
    void Start()
    {
        if(audioClips == null || audioClips.Length == 0)
        {
            Debug.Log("* 出错");
        }
    }

    // Update is called once per frame
    void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {
            NextSong();
        }
    }

    void NextSong()     // 实现随机播放歌曲
    {
        int index = Random.Range(0, audioClips.Length);
        AudioSource ad = GetComponent<AudioSource>();
        ad.clip = audioClips[index];
        ad.Play();           // Play是AudioSource的方法,而AudioSource一次只能装一个音频

        Debug.Log("** 播放第" + index + "首:" + audioClips[index].name);
    }
}

换汤不换药,挂在cube上

public class materialsChange : MonoBehaviour
{
    public Material[] material;
    int index = 0;

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            colorChange();
        }
    }

    void colorChange()
    {
        MeshRenderer ad = GetComponent<MeshRenderer>();
        ad.material = material[index];
        index += 1;
        if(index >= material.Length)
            index = 0;
    }
}

红绿灯

public class LightLogic : MonoBehaviour
{
    [Tooltip("红、黄、绿颜色指定")]
    public Material[] materials;
    int index = 0;
    // Start is called before the first frame update
    void Start()
    {
        ChangeColor();
    }

    void ChangeColor()
    {
        Transform child = this.transform.GetChild(1);
        MeshRenderer ad = child.gameObject.GetComponent<MeshRenderer>();
        ad.material = materials[index];
        if (index == 0)
        {
            Invoke("ChangeColor", 4);
        }
        if (index == 1)
        {
            Invoke("ChangeColor", 1);
        }
        if (index == 2)
        {
            Invoke("ChangeColor", 4);
        }
        if (index < 2)
            index += 1;
        else
            index = 0;
    }
}

project6

项目界面

火控系统(预制体的使用)

实现子弹的特性(有射程,有速度)

public class BulletLogic : MonoBehaviour           // BulletLogic 记录并实现子弹的特性:飞行速度、射程
{
    public float speed = 1;
    public float maxDistance = 2000;
    // Start is called before the first frame update
    void Start()
    {
        float lifeTime = maxDistance / speed; 
        Invoke("SelfDestroy", lifeTime);
    }

    // Update is called once per frame
    void Update()
    {
        Vector3 globalSpeed = new Vector3(0, 0, speed);
        //Vector3 localSpeed = transform.InverseTransformVector(globalSpeed);    一些瞎写
        transform.Translate(globalSpeed*Time.deltaTime, Space.Self);
    }

    void SelfDestroy()
    {
        Object.Destroy(this.gameObject);
    }

}

火控系统脚本

public class FireLogic : MonoBehaviour     // 火控代码,集合从炮台到子弹的所有参数
{
    public GameObject bulletPrefab;
    public Transform parent;
    public Transform firePoint;
    public Transform cannon;
    public float bulletSpeed;
    public float maxDistance;
    public float rotateSpeed;

    Vector3 eulerangle;

    // Start is called before the first frame update
    void Start()
    {
        Debug.Log(cannon.transform.eulerAngles);
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            TestFire();
        }

        float horizontalInput = Input.GetAxis("Horizontal");
        // float verticalInput = Input.GetAxis("Vertical");

        //loat currentXRotation = cannon.transform.eulerAngles.x;

        //if (currentXRotation >= -30 && currentXRotation <= 30 )        // 有问题,因为无论如何都会超出一点点,所以这样判断会导致控制不了
        //{                                                              // 所以要拆开来,向上抬过头就禁用向上抬,但能用向下转
            // cannon.Rotate(-verticalInput * rotateSpeed * Time.deltaTime, horizontalInput * rotateSpeed * Time.deltaTime, 0, Space.World);
        //}

        cannon.transform.Rotate(0, horizontalInput * rotateSpeed * Time.deltaTime, 0, Space.World);

        float delta = rotateSpeed * Time.deltaTime;

        if(Input.GetKey(KeyCode.S))
        {
            if (eulerangle.x < 30)
                eulerangle.x += delta;
        }

        if (Input.GetKey(KeyCode.W))
        {
            if (eulerangle.x > -30)
                eulerangle.x -= delta;
        }
        Vector3 term = cannon.transform.eulerAngles; // 利用term暂存canon的欧拉角,实现保存上面绕y轴的旋转,
        term.x = eulerangle.x;                       // 然后用eulerangle记录上下旋转的变化量
        cannon.transform.eulerAngles = term;

        Debug.Log("*" + eulerangle);  // x坐标显示的是-30到30
        // Debug.Log(currentXRotation);   欧拉角日志显示的是0到30,360到330
        Debug.Log("**" + cannon.transform.eulerAngles);
    }

    void TestFire()
    {

        GameObject Bullet = Object.Instantiate(bulletPrefab, parent);
        Bullet.transform.position = firePoint.position;  // 因为不在同一父节点下面,所以使用世界坐标

        Transform paoTa = transform.Find("/炮塔");
        Bullet.transform.eulerAngles = paoTa.eulerAngles;

        BulletLogic script = Bullet.GetComponent<BulletLogic>();
        script.speed = this.bulletSpeed;
        script.maxDistance = maxDistance;
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值