Unity开发一个FPS游戏之二

这篇具有很好参考价值的文章主要介绍了Unity开发一个FPS游戏之二。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在之前的文章中,我介绍了如何开发一个FPS游戏,添加一个第一人称的主角,并设置武器。现在我将继续完善这个游戏,打算添加敌人,实现其智能寻找玩家并进行对抗。完成的效果如下:

fps_enemy_demo

 

下载资源

首先是设计敌人,我们可以在网上找到一些好的免费素材,例如在Unity商店里面有一个不错的免费素材, Low Poly Soldiers Demo | 3D 角色 | Unity Asset Store,里面提供了3D模型和动画,其收费版提供了更多的模型,武器以及动画,收费10美刀也不贵。这里我以收费版为素材,把其加到我之前开发的FPS游戏中。

下载资源后导入到项目中,然后在项目文件的LowPoly Soldiers的prefab目录下,可以看到有多个不同服装和武器的Prefab。选择一个拖动到项目中的Prefab目录。然后打开Prefab,在模型中找到其武器,在其枪口位置新增一个名为muzzle的空的GameObject,这将作为敌人发射子弹的位置。如下图:

Unity开发一个FPS游戏之二,游戏开发,unity,游戏,游戏引擎,c#

点击这个Prefab的根对象,即左侧导航树的Soldier_marine variant,为其增加一个Capsule collider,调整其设置,使得这个Collider能覆盖整个人物。

增加一个Script的组件,重用之前创建的MuzzleEffect脚本。

添加一个Nav Mesh Agent组件,使得敌人能在烘培的路面上具备自动寻路的功能。

烘培路线

要让敌人能自动寻路,需要在场景中进行路线的烘培。打开菜单的Window->AI->Navigation,选择Bake,设置Agent的相关参数,然后点击Bake按钮即可。

定义敌人状态和动画切换

在项目的Scripts目录新增一个名为WanderingAI的Script文件,定义敌人的行为模式。这里我们可以定义敌人有行走,快跑,瞄准,射击,重载弹药,受伤害和死亡这几种行为。为了方便起见,在这个Script里面可以定义一个enum来统一管理当前的状态,如以下代码:

[Flags]
private enum EnemyStatus {
    Idle, 
    Walk,
    Aim,
    AimLeft,
    AimRight,
    Shoot,
    Reload,
    Sprint,
    Damage,
    Death
}

在以上状态中,瞄准状态有三个,分别对应原地瞄准,向左移动瞄准和向右移动瞄准。

另外再定义其他的一些属性,并进行初始化,如以下代码:

[Header("Enemy speed")]
public float speed = 3.0f;
public float sprintSpeed = 5.0f;

[Header("Enemy eyeview")]
public float eyeviewDistance = 500.0f;
public float viewAngle = 120f;
public float obstacleRange = 1.0f;

[Header("Enemy behavior")]
public float aimPeriod = 3.0f;

[Header("Shoot Setting")]
[SerializeField]
private Transform muzzleTransform;
[SerializeField] GameObject bulletPrefab;
public int ammo = 5;

[Header("Enemy life")]
public int health = 10;

private Animator _animator;
private Collider[] SpottedPlayers;
private GameObject bullet;
private MuzzleEffect muzzleEffect;
private long shootTS = 0;
private Vector3 prevPlayerPosition = new Vector3(100f, 100f, 100f);
private Vector3 enemyMuzzleDelta = new Vector3(0f, 2.0f, 0f);
private Vector3 playerPositionDelta = new Vector3(0f, 1.0f, 0f);
private Vector3 playerDirection;
private int currentAmmo; 
private float lerpValue = 10f;
private bool randomSearch = true;
private NavMeshAgent _agent;
private Coroutine corDiscoverPlayer = null;
private Coroutine corReload = null;
private Vector3 attackDirection;
private EnemyStatus status;
private EnemyStatus prevStatus;

void Start()
{
    _animator = GetComponent<Animator>();
    muzzleEffect = GetComponent<MuzzleEffect>();
    _agent = GetComponent<NavMeshAgent>();
    currentAmmo = ammo;
    status = EnemyStatus.Idle;
}

在项目的Animator目录,新建一个Animator controller文件,把LowPoly Soldiers的animation目录下需要用到的动画拖动到窗口,定义动画状态的切换。

Unity开发一个FPS游戏之二,游戏开发,unity,游戏,游戏引擎,c#

 在上图的左侧定义了相关的参数,主要是Trigger类型的,用于切换不同的动画状态。其中还有两个参数是Bool类型,用于判断动画是否播放完毕。在动画切换的Transition中,如果是由运动的动画切换到相对静止的动画,例如从Aim_Left切换到Shoot,需要开启Has Exit Time选项,这样就能平滑的过渡。如果其他不需要平滑过渡,需要立即切换状态的,则Transition不要选择Has Exit Time。

对于Damage和Reload这几个动画,需要添加一个Script,里面的OnStateExit函数需要设置animator.SetBool("E_IsDamage", false); 或者animator.SetBool("E_IsReload", false); 这样我们可以通过获取相应的参数来判断是否播放完毕。

检测玩家

当敌人没有发现玩家时,敌人处于随机搜索状态,这时敌人步行巡逻,当将要碰到障碍物时随机转向,如以下代码:

void Walk() {
    float distance = DetectObstacle(transform.forward);
    if (distance < obstacleRange) {
        float angle = UnityEngine.Random.Range(-110, 110);
        transform.Rotate(0, angle, 0);
    }
    transform.Translate(0, 0, speed * Time.deltaTime);
}

float DetectObstacle(Vector3 direction) {
    Ray ray = new Ray(transform.position, direction);
    RaycastHit hit;
    if (Physics.SphereCast(ray, 0.75f, out hit)) {
        return hit.distance;
    } else {
        return 9999.0f;
    }
}

在敌人巡逻的过程中,将检测其前方目视范围内是否有发现玩家,我们可以使用Physics.OverlapSphere函数,将检测以敌人当前位置为圆心,以视野范围为半径的球体内满足一定条件的所有碰撞体的集合,然后判断检测到的物体是否处于敌人的视野范围内,并且中间没有障碍物阻挡,如果满足条件,意味着敌人发现了玩家。然后我们可以进一步扩展这个检测之后触发的状态,当敌人发现玩家,我们可以进一步判断,是要先进入瞄准状态还是直接进入射击状态。考虑到这个检测是每次Update都调用的,如果在上一帧调用时第一次检测到了玩家,那么应该先进入瞄准模式,当下一帧调用时同样检测到了玩家,这时就不需要再次进入瞄准模式了,只需等待一段时间后进入射击模式即可。因此我们可以在每次检测到玩家时保存其位置,和之前保存的位置做比较,如果这个位置之间相差的范围超过一个阈值,则需要重新瞄准,否则的话直接射击。

如果没有检测到玩家,那么也分两种情况,一种情况是之前曾经检测过玩家,并保存了玩家的位置,这意味着玩家躲避敌人的攻击,从视野范围中消失。这时敌人应该迅速跑到玩家之前的位置,继续搜索。另一种情况就是之前没有检测到玩家,这时应该继续步行随机搜索的状态。

以下是检测玩家的代码:

void DetectPlayer() {
    Vector3 position = transform.position;
    SpottedPlayers = Physics.OverlapSphere(transform.position, eyeviewDistance, LayerMask.GetMask("Character"));
    
    for (int i=0;i<SpottedPlayers.Length;i++) {
        Vector3 playerPosition = SpottedPlayers[i].transform.position;

        if (Vector3.Angle(transform.forward, playerPosition - position) <= viewAngle/2) {
            RaycastHit info = new RaycastHit();
            int layermask = LayerMask.GetMask("Character", "Default");
            Physics.Raycast(position, playerPosition - position, out info, eyeviewDistance, layermask);
            if (info.collider == SpottedPlayers[i]) {
                randomSearch = false;
                playerDirection = playerPosition - prevPlayerPosition;
                float distance = (playerDirection).magnitude;
                if (distance > 0.5f || prevStatus != EnemyStatus.Shoot) {
                    prevPlayerPosition = playerPosition;
                    switch(UnityEngine.Random.Range(1, 4)) {
                        case 1:
                            _animator.SetTrigger("E_Aim");
                            status = EnemyStatus.Aim;
                            break;
                        case 2:
                            if (DetectObstacle(-transform.right) >= 3.0f) {
                                _animator.SetTrigger("E_Aim_L");
                                status = EnemyStatus.AimLeft;
                            } else {
                                _animator.SetTrigger("E_Aim");
                                status = EnemyStatus.Aim;
                            }
                            break;
                        case 3:
                            if (DetectObstacle(transform.right) >= 3.0f) {
                                _animator.SetTrigger("E_Aim_R");
                                status = EnemyStatus.AimRight;
                            } else {
                                _animator.SetTrigger("E_Aim");
                                status = EnemyStatus.Aim;
                            }
                            break;
                    }
                }
                _agent.isStopped = true;
                if (status == EnemyStatus.Aim || status == EnemyStatus.AimLeft) {
                    corDiscoverPlayer = StartCoroutine(DiscoverPlayer());
                } else {
                    StartCoroutine(DiscoverPlayer());
                }
                return;
            }
        }
    }

    // Can not detect player
    if (randomSearch) {
        if (status != EnemyStatus.Walk) {
            _animator.SetTrigger("E_Walk");
            status = EnemyStatus.Walk;
        }
    } else {
        if (status != EnemyStatus.Sprint) {
            _animator.SetTrigger("E_Sprint");
            status = EnemyStatus.Sprint;
            _agent.destination = prevPlayerPosition - playerPositionDelta;
            _agent.isStopped = false;
            _agent.speed = sprintSpeed;
        }
    }

    if (corDiscoverPlayer != null) {
        StopCoroutine(corDiscoverPlayer);
    }
    return;
}

在以上代码中,当检测到玩家时,敌人将进入瞄准或射击状态并启动一个协程运行DiscoverPlayer,采用协程的原因是,需要瞄准一段时间之后才能射击。在Shoot函数中,会判断当前的弹药,如果为0,则启动一个协程运行Reload。如果弹药不为0,则按照一定的射速来发射子弹。另外,考虑到游戏难度,特意在发射子弹时,给其位置增加一点随机的小的偏移量,这样即使敌人瞄准了玩家,也不一定能打准。其代码如下:

private IEnumerator DiscoverPlayer() {
    if (status == EnemyStatus.Aim || status == EnemyStatus.AimLeft || status == EnemyStatus.AimRight) {
        yield return new WaitForSecondsRealtime(aimPeriod);
    }
    Shoot();
}

private void Shoot() {
    if (currentAmmo==0) {
        corReload = StartCoroutine(Reload());
        return;
    }
    long nowTS = DateTime.UtcNow.Ticks;
    float shootInterval = (nowTS - shootTS)/10000000.0f;
    status = EnemyStatus.Shoot;
    if (shootInterval >= 0.5) {
        TurnToPlayer();
        _animator.SetTrigger("E_Shoot");
        Vector3 bulletPosition = new Vector3(UnityEngine.Random.Range(-0.1f, 0.1f), UnityEngine.Random.Range(-0.1f, 0.1f), 0f) + muzzleTransform.position;
        bullet = Instantiate(bulletPrefab, bulletPosition, transform.rotation);
        Vector3 tempForward = prevPlayerPosition - muzzleTransform.position;
        bullet.GetComponent<Rigidbody>().velocity = new Vector3(tempForward.x, 0f, tempForward.z) * 5.0f;
        muzzleEffect.Effect(muzzleTransform.position);
        shootTS = nowTS;
        currentAmmo--;
    }
}

private IEnumerator Reload() {
    _animator.SetTrigger("E_Reload");
    status = EnemyStatus.Reload;
    _animator.SetBool("E_IsReload", true);
    yield return new WaitUntil(()=>!_animator.GetBool("E_IsReload"));
    currentAmmo = ammo;
    status = EnemyStatus.Idle;
}

 如果没有检测到玩家,则判断当前是否要随机搜索,如果是,则设置状态为Walk并进行搜索,如果不是,意味着之前是有检测到玩家的,则根据之前保存的玩家的位置,快跑到该位置,这里是采用Nav Mesh Agent的自动导航功能来实现。

射击敌人

在上一篇博客中,我们定义了玩家可以发射子弹,并且进行碰撞判断,那么当碰撞的物体是敌人时,应该扣除敌人的生命值。

修改之前的Bullet.cs程序的OnCollisionEnter,另外要把敌人预制件的Tag设置为Enemy

private void OnCollisionEnter(Collision collision) {
    if (collision.gameObject.CompareTag("Enemy")) {
        WanderingAI behavior = collision.gameObject.GetComponent<WanderingAI>();
        behavior.TakeDamage(damage, transform.forward);
    }
    Destroy(this.gameObject);
}

在WanderingAI.cs中,增加一个TakeDamage函数和相应的其他函数,当受到伤害时,需要立即切换到受伤害的动画,之前如果有未完成的Reload或者DiscoverPlayer的协程,需要立即关闭。如果敌人的health为0,则切换到Death的动画,并在等待一段时间后销毁该Gameobject。如以下代码:

public void TakeDamage(int damage, Vector3 direction) {
    if (health > 0) {
        health -= damage;
        attackDirection = -direction;
        if (corDiscoverPlayer != null) {
            StopCoroutine(corDiscoverPlayer);
            corDiscoverPlayer = null;
        }
        if (corReload != null) {
            StopCoroutine(corReload);
            corReload = null;
        }
        
        if (health <= 0) {
            StartCoroutine(Death());
        } else {
            Damage();
        }
    }
}

private void Damage() {
    switch(UnityEngine.Random.Range(1, 4)) {
        case 1:
            _animator.SetTrigger("E_Damage_A");
            break;
        case 2:
            _animator.SetTrigger("E_Damage_B");
            break;
        default:
            _animator.SetTrigger("E_Damage_C");
            break;
    }
    _animator.SetBool("E_IsDamage", true);
    status = EnemyStatus.Damage;
}

private IEnumerator Death() {
    switch(UnityEngine.Random.Range(1, 4)) {
        case 1:
            _animator.SetTrigger("E_Death_A");
            break;
        case 2:
            _animator.SetTrigger("E_Death_B");
            break;
        default:
            _animator.SetTrigger("E_Death_C");
            break;
    }
    status = EnemyStatus.Death;
    yield return new WaitForSecondsRealtime(6.0f);
    Destroy(this.gameObject);
}

状态处理

定义了以上关键的检测玩家和瞄准射击的功能,以及敌人的不同状态后,我们可以在Update函数里面根据不同的状态来调用不同的功能。如以下代码:

void Update()
{        
    if (status == EnemyStatus.AimLeft || status == EnemyStatus.AimRight) {
        AimMove();
    } else if (status == EnemyStatus.Reload || status == EnemyStatus.Death) {
        return;
    } else if (status == EnemyStatus.Aim) {
        TurnToPlayer();
    } else if (status == EnemyStatus.Damage) {
        TurnToDamage();
        if (!_animator.GetBool("E_IsDamage")) {
            status = EnemyStatus.Idle;
            prevPlayerPosition = new Vector3(100f, 100f, 100f);
        }
    } else {
        if (status == EnemyStatus.Walk) {
            Walk();
        }
        if (status == EnemyStatus.Sprint) {
            Sprint();
        }
        DetectPlayer();
    }
    prevStatus = status;
}

void TurnToPlayer() {
    Vector3 targetDirection = (prevPlayerPosition - transform.position).normalized;
    transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(targetDirection), lerpValue*Time.deltaTime);
}

void TurnToDamage() {
    transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(attackDirection), lerpValue*Time.deltaTime);
}

void AimMove() {
    TurnToPlayer();
    float distance = 0f;
    if (status == EnemyStatus.AimLeft) {
        distance = DetectObstacle(-transform.right);
    }
    if (status == EnemyStatus.AimRight) {
        distance = DetectObstacle(transform.right);
    }
    if (distance < 0.5) {
        _animator.SetTrigger("E_Aim");
        status = EnemyStatus.Aim;
    } else {
        if (status == EnemyStatus.AimLeft) {
            transform.Translate(speed * Time.deltaTime * -Vector3.right);
        }
        if (status == EnemyStatus.AimRight) {
            transform.Translate(speed * Time.deltaTime * Vector3.right);
        }
    }
}

void Sprint() {
    if (_agent.remainingDistance < 3.0f) {
        _agent.isStopped = true;
        randomSearch = true;
        _animator.SetTrigger("E_Walk");
        status = EnemyStatus.Walk;
        transform.forward = playerDirection;
    }
}

解释一下以上的代码,当瞄准状态为AimLeft或者AimRight的时候,因为需要向旁边移动,所以需要先判断一下距离旁边障碍物的距离是否大于一个阈值,如果不是则把状态设置为静止的Aim。当状态为Sprint的时候,敌人将快速跑到玩家之前出现的位置,当距离这个位置很接近时,将停止并切换为随机搜索状态。

设置玩家生命值

当敌人发射子弹的时候,如果子弹碰撞到玩家,那么玩家的生命值需要扣减。我们需要在游戏界面上用显示当前的生命值,可以用一个血槽来显示。

在上一篇博客中,我们创建了一个名为GameScreen的Canvas预制体,在其上显示当前的弹药数。同样我们需要在这个预制体里面增加生命值的显示控件。在这个预制体上创建一个新的UI->Slider,命名为Health。设置其Scale X和Y都为3。

在Assests的Images目录下,导入一个新的Asset,选择一张16*16大小的白色图片,类型选择为UI Sprite。

点击Health slider下的background,颜色RGB都设置为0,Alpha设置为102。

点击Fill Area下的Fill,拖动之前导入的白色图片到Source Image,Color设置为红色。然后把Fill从Fill Area的子物体拖动到上一层,和Fill Area平级。

现在改动Health slider的Value,可以看到红色血量可以跟随Value值变动,但是可以看到Fill的区域和Background的区域没有对齐。 分别选择Fill和Background,点击Rect Transform的Stretch按钮,然后按着Alt键选择右下方的Stretch。这样两个区域就能对齐。

最后可以在添加一个十字Icon的Image在这个血槽的左边,以达到更美观的效果。

增加一个新的消息类型来传递当前的生命值给新加的Health UI组件。编辑GameMessage.cs文件,增加一个新的消息,如下:

public interface IGameMessage : IEventSystemHandler
{
    ...
    void HealthMessage(float currentHealth);
}

 编辑UIController.cs文件,增加对Health组件的引用和消息的处理

public class UIController : MonoBehaviour, IGameMessage
{
    ...
    [SerializeField] Slider playerHealth;


    public void HealthMessage(float health) {
        playerHealth.value = health;
    }
}

编辑PlayerController.cs文件,增加一个TakeDamage方法来扣减Health

    [Header("Player")]
    ...
    [Tooltip("Player health")]
    public int PlayerHealth = 10;

    ...
    private int _health;

    private void Start()
    {
        ...
        _health = PlayerHealth;
        ExecuteEvents.Execute<IGameMessage>(_gameManager, null, (x,y)=>x.HealthMessage(1.0f));
    }

    public void TakeDamage(int damage) {
        _health -= damage;
        float healthValue = (float) _health/PlayerHealth;
        ExecuteEvents.Execute<IGameMessage>(_gameManager, null, (x,y)=>x.HealthMessage(healthValue));
    }

最后就是修改bullet.cs,当判断碰撞的物体Tag是Player时,调用PlayerController的TakeDamage方法。

private void OnCollisionEnter(Collision collision) {
    ...
    if (collision.gameObject.CompareTag("Player")) {
        PlayerController player = collision.gameObject.GetComponent<PlayerController>();
        player.TakeDamage(damage);
    }
    Destroy(this.gameObject);
}

 现在生命值的处理就完成了。

玩家受到攻击的闪红处理

通常当玩家受到伤害时,屏幕都会用红色闪现一下,代表玩家掉血。现在继续完善添加这个效果。

在GameScreen下增加一个新的Canvas,名字为FeedbackFlashCanvas,然后在其下新增一个名为FlashImage的Canvas Group,Source Image的color为红色,Alpha为255。

在GameScreen新增一个Script组件,名字为FeedbackFlash.cs,代码如下:

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

public class FeedbackFlash : MonoBehaviour
{
    [Header("References")] [Tooltip("Image component of the flash")]
    public Image FlashImage;

    [Tooltip("CanvasGroup to fade the damage flash, used when recieving damage end healing")]
    public CanvasGroup FlashCanvasGroup;

    [Header("Damage")] [Tooltip("Color of the damage flash")]
    public Color DamageFlashColor;

    [Tooltip("Duration of the damage flash")]
    public float DamageFlashDuration;

    [Tooltip("Max alpha of the damage flash")]
    public float DamageFlashMaxAlpha = 1f;

    bool m_FlashActive;
    float m_LastTimeFlashStarted = Mathf.NegativeInfinity;

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if (m_FlashActive) {
            float normalizedTimeSinceDamage = (Time.time - m_LastTimeFlashStarted) / DamageFlashDuration;

            if (normalizedTimeSinceDamage < 1f)
            {
                float flashAmount = DamageFlashMaxAlpha * (1f - normalizedTimeSinceDamage);
                FlashCanvasGroup.alpha = flashAmount;
            }
            else
            {
                FlashCanvasGroup.gameObject.SetActive(false);
                m_FlashActive = false;
            }
        }
    }

    void ResetFlash()
    {
        m_LastTimeFlashStarted = Time.time;
        m_FlashActive = true;
        FlashCanvasGroup.alpha = 0f;
        FlashCanvasGroup.gameObject.SetActive(true);
    }

    public void OnTakeDamage()
    {
        ResetFlash();
        FlashImage.color = DamageFlashColor;
    }
}

把刚才建的FlashImage拖动到Script的相应属性。Damage Flash Color设置为红色,Alpha为255,Flash Duration设置为0.2, Flash max alpha设置为0.7 

回到UIController.cs这个脚本,在收到HealthMessage的时候调用FeedbackFlash的OnTakeDamage,这样就可以出现红色闪烁的效果。

public class UIController : MonoBehaviour, IGameMessage
{
    ...
    private FeedbackFlash _flash;

    void Start()
    {
        _flash = GetComponent<FeedbackFlash>();
    }

    public void HealthMessage(float health) {
        ...
        _flash.OnTakeDamage();
    }
}

总结

以上就是对这个FPS游戏增加了具备智能行为的敌人的相关设计介绍,可以看到整个游戏的可玩性有了进一步的提高。下一步将进行关卡的设计,完善游戏场景。文章来源地址https://www.toymoban.com/news/detail-840863.html

到了这里,关于Unity开发一个FPS游戏之二的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • unity制作FPS射击游戏

    角色动作方面包括行走、奔跑、跳跃、武器切换、弹夹更换、武器展示、弹壳抛出效果、射击效果、全自动与半自动射击效果、瞄准效果、后坐力效果、弹痕效果等多种动作。 非玩家角色(NPC)具备多个动画状态,包括固定路径巡逻、行走、奔跑、寻路攻击等多种行为。 太

    2024年02月08日
    浏览(58)
  • 使用团结引擎开发Unity 3D射击游戏

           本案例是初级案例,意在引导想使用unity的初级开发者能较快的入门,体验unity开发的方便性和简易性能。       本次我们将使用团结引擎进行开发,帮助想体验团结引擎的入门开发者进行较快的环境熟悉。      本游戏是一个俯视角度的射击游戏。主角始终位于屏幕

    2024年01月19日
    浏览(78)
  • Unity、UE、Cocos游戏开发引擎的区别

    Unity、Unreal Engine(UE)和Cocos引擎是三个常用的游戏开发引擎,它们在功能和特性上有一些区别。以下是它们之间的主要区别: 编程语言:Unity使用C#作为主要的编程语言,开发者可以使用C#脚本进行游戏逻辑编写。Unreal Engine主要使用C++作为编程语言,但也支持蓝图系统,允许

    2024年02月22日
    浏览(66)
  • 【Unity实战】实现一款简单的FPS游戏

    实现一款FPS游戏需要以下步骤: 1.创建场景:在Unity中创建3D场景,设定地形、灯光、天气等环境,新增角色、武器等道具。 2.角色控制:创建角色,并添加Unity内置的角色控制器或自定义控制器脚本,处理角色的移动、射击、跳跃、动作等。 3.武器系统:创建武器模型,添加

    2024年02月06日
    浏览(45)
  • Unity游戏源码分享-射击游戏Low Poly FPS Pack 3.2

    Unity游戏源码分享-射击游戏Low Poly FPS Pack 3.2 项目地址:https://download.csdn.net/download/Highning0007/88057717    

    2024年02月16日
    浏览(56)
  • Unity 开发人员转CGE(castle Game engine)城堡游戏引擎指导手册

    一、简介 2. Unity相当于什么GameObject? 3. 如何设计一个由多种资产、生物等组成的关卡? 4. 在哪里放置特定角色的代码(例如生物、物品)?Unity 中“向 GameObject 添加 MonoBehaviour”相当于什么? 5.Unity子目录相当于什么Assets? 6. 支持哪些模型格式? 7. 支持FBX模型格式吗? 8.

    2024年02月07日
    浏览(79)
  • 【制作100个unity实战之2】实现一款简单的FPS游戏

    实现一款FPS游戏需要以下步骤: 1.创建场景:在Unity中创建3D场景,设定地形、灯光、天气等环境,新增角色、武器等道具。 2.角色控制:创建角色,并添加Unity内置的角色控制器或自定义控制器脚本,处理角色的移动、射击、跳跃、动作等。 3.武器系统:创建武器模型,添加

    2024年02月04日
    浏览(67)
  • 【Unity】Unity开发学习和项目实践02——创建第一个Unity项目和游戏物体

    创建第1个Unity项目 打开Unity hub,点击新项目 以下有四处地方需要注意选择: 1.Unity编辑器版本 2.项目模板 3.项目名称 4.项目保存位置 点击创建项目 ok,进入编辑器了 把编辑器界面布局稍微改一下,改成2by3 点击Edit 点击 project settings,这是对我们所创建工程的设置 此外还有对

    2024年01月25日
    浏览(56)
  • 【unity小技巧】两种办法解决FPS游戏枪或者人物穿墙穿模问题

    当我们开发FPS游戏时(其实3d游戏基本都会遇到这样的问题),如果我们不做处理,肯定会出现人物或者枪的穿墙穿模问题,这是是一个常见的挑战。 这种问题会破坏游戏的真实性和可玩性,使玩家的体验受到影响。在这篇文章中,我将分享一些Unity小技巧,介绍如何解决F

    2024年02月03日
    浏览(74)
  • 游戏开发常用引擎工具介绍对比区别(UE4,Unity,Cocos,LayaAir,[egret-白鹭])

    是一套为开发实时技术而存在的引擎工具。目前广泛应用于3D建模渲染、游戏开发中。它完善的工具套件以及简易的工作流程能够使开发者快速修改或查看成果,对于代码的依赖性很低。而完整公开的源代码则能让使用者自由修改和扩展引擎功能。 是面向开发人员的 3D/2D 游戏

    2024年02月13日
    浏览(68)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包