【用unity实现100个游戏之17】从零开始制作一个类幸存者肉鸽(Roguelike)游戏3(附项目源码)

简介: 【用unity实现100个游戏之17】从零开始制作一个类幸存者肉鸽(Roguelike)游戏3(附项目源码)

本节最终效果

前言

本节紧跟着上一篇,主要实现武器功能。

近战武器

新增Bullet,子弹脚本

public class Bullet : MonoBehaviour
{
    public float damage; // 子弹的伤害值

    /// <summary>
    /// 初始化子弹的属性
    /// </summary>
    /// <param name="damage">伤害值</param>
    public void Init(float damage)
    {
        this.damage = damage; // 设置子弹的伤害值
    }
}

新增近战武器预制体,添加触发器,挂载脚本,配置参数

修改Enemy敌人脚本,添加触发器检测

void OnTriggerEnter2D(Collider2D collision)
{
    if (!collision.CompareTag("Bullet")) return;
    health -= collision.GetComponent<Bullet>().damage;
    if (health > 0)
    {

    }
    else
    {
        Dead();
    }
}

void Dead()
{
    gameObject.SetActive(false);
}

效果,敌人触碰死亡

控制近战武器生成

配置近战武器进对象池

新增Weapon。控制武器/子弹的生成

public class Weapon : MonoBehaviour
{
    public int id; // 武器/子弹的ID
    public int prefabId; // 武器/子弹预制体的ID
    public float damage; // 武器/子弹的伤害值
    public int count; // 发射武器/子弹的数量
    public float speed; // 武器旋转的速度

    void Start()
    {
        Init(); // 初始化武器属性
    }

    void Update()
    {
        switch (id)
        {
            case 0:
                transform.Rotate(Vector3.back, speed * Time.deltaTime); // 绕z轴旋转武器
                break;
            default:
                break;
        }
    }

    /// <summary>
    /// 初始化武器/子弹属性
    /// </summary>
    public void Init()
    {
        switch (id)
        {
            case 0:
                speed = 150; // 设置武器旋转的速度
                Batch();
                break;
            default:
                break;
        }
    }

    /// <summary>
    /// 批量配置武器属性
    /// </summary>
    void Batch()
    {
        for (int index = 0; index < count; index++)
        {
            Transform bullet = GameManager.instance.pool.Get(prefabId).transform; // 从对象池中获取武器预制体的实例
            bullet.parent = transform; // 设置武器的父级为武器

            Vector3 rotvec = Vector3.forward * 360 * index / count; // 计算武器的旋转角度,使其均匀分布在圆周上
            bullet.Rotate(rotvec); // 设置武器的旋转角度
            bullet.Translate(bullet.up * 1.5f, Space.World); // 将武器沿着自身的y轴方向向前移动一定距离

            bullet.GetComponent<Bullet>().Init(damage); // 初始化武器的属性
        }
    }
}

挂载配置参数

单武器效果

多武器效果

升级增加武器伤害和数量

修改Weapon

void Update()
{
    //。。。

    //测试
    if (Input.GetKeyDown(KeyCode.Space))
    {
        LevelUp(20, 5);
    }
}

public void LevelUp(float damage, int count)
{
    this.damage = damage * Character.Damage;
    this.count += count;
    if (id == 0) Batch();
}

/// <summary>
/// 批量配置武器属性
/// </summary>
void Batch()
{
    for (int index = 0; index < count; index++)
    {
        Transform bullet;
        if (index < transform.childCount)
        {
            bullet = transform.GetChild(index);
        }
        else
        {
            bullet = GameManager.instance.pool.Get(prefabId).transform;// 从对象池中获取武器预制体的实例
            bullet.parent = transform;// 设置武器的父级为武器
        }
        
        //。。。
    }
}

效果

查找离主角最近的敌人

新增Scanner

public class Scanner : MonoBehaviour
{
    public float scanRange; // 扫描范围
    public LayerMask targetLayer; // 目标层
    public RaycastHit2D[] targets; // 扫描到的目标
    public Transform nearestTarget; // 最近的目标

    void FixedUpdate()
    {
        targets = Physics2D.CircleCastAll(transform.position, scanRange, Vector2.zero, 0, targetLayer); // 在扫描范围内发射圆形光线,获取扫描到的目标
        nearestTarget = GetNearest();
    }

    // 获取最近的目标
    Transform GetNearest()
    {
        Transform result = null; // 初始化最近的目标为null
        float diff = scanRange; // 初始化最小距离为100,用于后面比较
        foreach (RaycastHit2D target in targets)
        {
            Vector3 myPos = transform.position; // 获取自身位置
            Vector3 targetPos = target.transform.position; // 获取目标位置
            float curDiff = Vector3.Distance(myPos, targetPos); // 计算自身与目标的距离
            if (curDiff < diff) // 如果当前目标距离比之前的最小距离还小,说明这个目标更近
            {
                diff = curDiff; // 更新最小距离
                result = target.transform; // 更新最近的目标
            }
        }
        return result; // 返回最近的目标
    }
    
    //场景窗口可视化射线
    private void OnDrawGizmos()
    {
        Gizmos.DrawWireSphere(transform.position, scanRange);
        Gizmos.color = Color.red;
    }
}

挂载脚本,配置参数

效果,获取了离自己最近的敌人

子弹预制体

对象池挂载子弹

生成子弹

修改Weapon

float timer;
Player player;

void Start()
{
    player = GameManager.instance.player;
    Init(); // 初始化武器属性
}

void Update()
{
    switch (id)
    {
        //。。。
        
        default:
            timer += Time.deltaTime;
            if (timer > speed)
            {
                timer = 0f;
                Fire();
            }
            break;
    }
}

/// <summary>
/// 初始化武器/子弹属性
/// </summary>
public void Init()
{
    switch (id)
    {
        //。。。
        
        default:
            speed = 0.3f;
            break;
    }
}

//发射子弹
void Fire()
{
    if (!player.scanner.nearestTarget) return;
    Transform bullet = GameManager.instance.pool.Get(prefabId).transform;
    bullet.position = transform.position;
}

修改Player,获取最近的敌人数据

[NonReorderable]
public Scanner scanner;

scanner = GetComponent<Scanner>();

配置子弹生成容器及数据

效果,每0.3秒生成一个子弹

发射子弹

修改Bullet

Rigidbody2D rigid;
private int per; // 子弹的穿透次数

void Awake()
{
    rigid = GetComponent<Rigidbody2D>(); // 获取子弹的刚体组件
}

public void Init(float damage, int per, Vector3 dir)
{
    this.damage = damage; // 初始化子弹的伤害值
    this.per = per; // 初始化子弹的穿透次数

    if (per > -1)
    {
        rigid.velocity = dir * 15f; // 如果子弹具有穿透次数,则设置子弹的初始速度和方向
    }
}

void OnTriggerEnter2D(Collider2D collision)
{
    if (!collision.CompareTag("Enemy") || per == -1) return; // 如果碰撞物体不是敌人或子弹没有穿透次数,直接返回

    per--; // 穿透次数减一

    if (per == -1)
    {
        rigid.velocity = Vector2.zero; // 如果穿透次数减到-1,即没有穿透能力了,将子弹的速度设为零
        gameObject.SetActive(false); // 将子弹设为非激活状态,使其隐藏或回收
    }
}

修改Weapon

void Batch()
{
    //。。。

        bullet.GetComponent<Bullet>().Init(damage, -1, Vector3.zero); // 初始化武器的属性
    }
}

// 发射子弹
void Fire()
{
    if (!player.scanner.nearestTarget) return; // 如果玩家的扫描器没有最近的目标,直接返回

    Vector3 targetPos = player.scanner.nearestTarget.position; // 获取最近目标的位置
    Vector3 dir = targetPos - transform.position; // 计算从当前位置指向目标位置的方向向量
    dir = dir.normalized; // 将方向向量归一化,使其成为单位向量

    Transform bullet = GameManager.instance.pool.Get(prefabId).transform; // 从对象池中获取一个子弹对象
    bullet.position = transform.position; // 设置子弹的初始位置为当前位置
    bullet.rotation = Quaternion.FromToRotation(Vector3.up, dir); // 将子弹的朝向调整为指向目标位置的方向
    bullet.GetComponent<Bullet>().Init(damage, count, dir); // 初始化子弹的伤害值、穿透次数和初始速度方向
}

效果

修改Weapon,升级测试

//测试
if (Input.GetKeyDown(KeyCode.Space))
{
    LevelUp(10, 1);
}

效果,升级后子弹具有了一定穿透性

参考

【视频】https://www.youtube.com/watch?v=MmW166cHj54&list=PLO-mt5Iu5TeZF8xMHqtT_DhAPKmjF6i3x

源码

源码在最后一节

目录
相关文章
|
4天前
|
存储 JSON 关系型数据库
【unity实战】制作unity数据保存和加载系统——大型游戏存储的最优解
【unity实战】制作unity数据保存和加载系统——大型游戏存储的最优解
12 2
|
4天前
|
存储 JSON 图形学
【unity实战】制作unity数据保存和加载系统——小型游戏存储的最优解
【unity实战】制作unity数据保存和加载系统——小型游戏存储的最优解
6 0
|
4天前
|
图形学
【制作100个unity游戏之29】使用unity复刻经典游戏《愤怒的小鸟》(完结,附带项目源码)(下)
【制作100个unity游戏之29】使用unity复刻经典游戏《愤怒的小鸟》(完结,附带项目源码)(下)
8 0
|
4天前
|
图形学
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版3(附带项目源码)
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版3(附带项目源码)
14 2
|
4天前
|
图形学
【制作100个unity游戏之28】花半天时间用unity复刻童年4399经典小游戏《黄金矿工》(附带项目源码)
【制作100个unity游戏之28】花半天时间用unity复刻童年4399经典小游戏《黄金矿工》(附带项目源码)
15 0
|
4天前
|
图形学
【制作100个unity游戏之29】使用unity复刻经典游戏《愤怒的小鸟》(完结,附带项目源码)(上)
【制作100个unity游戏之29】使用unity复刻经典游戏《愤怒的小鸟》(完结,附带项目源码)
10 2
|
4天前
|
图形学
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版2(附带项目源码)
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版2(附带项目源码)
8 1
|
4天前
|
存储 JSON 关系型数据库
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版13(完结,附带项目源码)
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版13(完结,附带项目源码)
9 0
|
4天前
|
图形学
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版12(附带项目源码)
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版12(附带项目源码)
9 0
|
4天前
|
存储 图形学
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版11(附带项目源码)
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版11(附带项目源码)
10 0