Unity百游修炼(3)——Tank_Battle(双人对战)详细制作全流程

news/2025/2/26 6:33:55

一.项目简介

        《Tank_Battle》是一款基于 Unity 引擎开发的 3D 双人对战坦克游戏。玩家在游戏中操控各自的坦克,通过灵活移动、旋转和发射子弹等操作,展开激烈的对战,旨在摧毁对方坦克以获得胜利。该项目结合了丰富的游戏元素和交互机制,为玩家带来沉浸式的游戏体验。

游戏画面如下:

Tank_battle


二.创建场景

1.创建3D工程(命名为Tank_Battle)

2.导入unity_package(资源包)到Project窗口中(资源在素材里面)

3.将文件夹Prefab中levelart场景)拖入到hierarchy窗口中

4.删除场景自带的直射光,因为levelart中自带了直射光

5.调节场景光线:Window——Reddering——Lighting——Environmentsource光源调成color颜色,颜色调成和环境贴合的颜色

6.Main camera改变天空场景颜色


三.添加坦克(加拖尾特效)

1.把models中Tank拖放到Hierarchy视图中

2.把prefab中的DustTrall拖入到Tank上,复制一个,选中两个拖尾,分别将两个拖尾移动到轮子履带处

3.拖尾位置如下

4.给tank添加Box collider组件

5.调节collider的大小

6.把tank变成prefab,直接把tank拖到Projectors——Prefab文件夹中(为了后面坦克多了统一管理)


四.控制坦克前后移动

1.创建控制坦克移动的脚本并命名为Tank_ Move(记得创建一个Scripst文件夹统一管理脚本)

2.选择Tank,添加刚体组件Rigidbody

3.进入Tank_Move脚本

4.获取Tank本身Rigidbody组件

4.1创建刚体变量,保存

public class TankMove : MonoBehaviour
{
    public Rigidbody Tank_body;//创建刚体变量接受Tank的刚体
    // Start is called before the first frame update
    void Start()
    {
        
    }

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

4.2把脚本拖放到Tank身上,然后把Tank脚本的Tank_body变量指定为Tank本身的Rigidbody

5.按键控制tank前后移动

5.1按键ws控制移动

5.2检测按键ws按下并保存按下的返回值

 Input.GetAxisRaw();作用是:检测是否有按键按下,并返回float值

"Vertical"作用是:竖直方向
 

5.3按键使得坦克向前或者向后移动

transform.forward 的含义

     方向表示:它是一个三维向量(Vector3 类型),指向游戏对象所面向的正前方。这个方向是相对于游戏对象的旋转而言的,当游戏对象的旋转发生改变时,transform.forward 的方向也会随之改变


五.控制坦克旋转

1.按键ad控制旋转

2.检测按键ad按下并保存按下的返回值

3.绕Y轴移动

        这行代码的主要功能是设置坦克刚体(Tank_body)的角速度,从而让坦克绕着轴进行旋转。旋转的轴是游戏对象自身的向上方向(transform.up),即Y轴,旋转的速度由 r 和 TankRotate_Speed 共同决定。

4.完整代码展现


六.控制两辆坦克(实现双人一起玩)

1.打开Edit——Projectsettings——input Manger

2.保证ad只控制Player1的旋转,左右箭头只控制Player2的旋转

2.1光标放到Horizontal上,右击创建两个新的Horizontal,分别命名为Horizontal_Player1,Horizontal_Player2

2.2玩家一删除下图圈出来的内容,表示取消左右箭头控制前进

2.3玩家二删除下图圈出来的内容,表示取消ad按键控制旋转

3.保证ws只控制Player1的前进后退,上下箭头只控制Player2的前进后退

3.1光标放到Vertical上,右击创建两个新的Vertical,分别命名为Vertical_Player1,Vertical_Player2

3.2玩家一删除下图圈出来的内容,表示取消上下箭头控制前进

3.3玩家二删除下图圈出来的内容,表示取消ws按键控制旋转


七.控制坦克发射子弹

1.将子弹创建出来,并实例化

1.1将子弹素材拖入到Hierarchy中,并拖入Prefab成为预制体,并添加胶囊碰撞器

1.2修改碰撞体大小(记得按下图操作,一定要改成z轴)

1.3实例化子弹——确定位置。创建一个空物体,用来确定子弹发射位置,空物体是倾斜的(因为炮管是倾斜的),命名为fireposition

2.添加子弹攻击脚本Tank_Shoot——实例化子弹

2.1创建脚本

2.2创建变量接受位置,同时选中每次创建的子弹

 public Transform firePosition;//接受发射子弹的位置物体
 public GameObject ShellPrefab;//接受每次创建的实例化子弹

2.3把脚本拖到发射位置物体上,然后把发射位置和发射的子弹预制体选中

2.4把发射位置物体,拖到Tank物体上,作为其子对象,这样就可以保证相对位置不变了

2.5空格键控制子弹发射

2.6控制子弹飞行速度

八.子弹爆炸效果

1.创建子弹脚本Sheet

2.接受爆炸效果的预制体

3.当子弹触发器触碰到其他物体时,触发爆炸效果

4.我增加了一个开火也可以有爆炸效果,模仿后坐力很大

九.控制子弹造成伤害

1.给tank加tag标签,区分子弹是否触碰到了tank

2.写脚本——tank受到伤害处理的脚本

public class Tank_health : MonoBehaviour
{
    public int hp = 100;//坦克血量
    public GameObject Tankbaozha;//坦克爆炸效果
    public AudioSource TankbaozhaAudioSource;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
    void HurtHealthy()
    {
        if (hp < 0)
        {
            return;
        }
        hp -= Random.Range(10, 20);//伤害是随机的 10  20
        if (hp < 0)//坦克没了
        {
            TankbaozhaAudioSource.Play();
            GameObject.Instantiate(Tankbaozha,transform.position+Vector3.up,transform.rotation);
            GameObject.Destroy(this.gameObject);
#if UNITY_EDITOR
            UnityEditor.EditorApplication.isPlaying = false;
#else
                Application.Quit();
#endif
        }
    }

3.tank触发到tank,调用2中生成的脚本

 public void OnTriggerEnter(Collider other)
 {
     if (SheelExplosionPrefab != null)
     {
         GameObject.Instantiate(SheelExplosionPrefab, transform.position, transform.rotation);
     }
     if(other.tag=="Tank")
     {
         other.SendMessage("HurtHealthy");//即调用的方法
     }
 }

十.相机跟随

原理:相机与两坦克的中心保持一定的向量关系,同时还有近大远小的视觉效果

1.算出两坦克的中心点与摄像机坐标的向量差。

2.保证摄像机始终与中心点有个相同向量差

3.当坦克间距变小时,摄像头视角变小;反之,摄像头变大


十一.添加音效,音乐

1.背景音乐

(1)创建一个空物体,命名为:Music

(2)在空物体上添加组件audio source

(3)在组件上添加背景音乐

(4)进行基础设置即可

2.子弹音效:每次发射都会启动
原理:一样的添加audio source组件用来存储音效,每次在实例化子弹的时候,启动音效

十二.问题

1.可能出现的问题1:Assembly-CSharp-Editor渲染函数老旧过时了。

解决方案:更换函数,下面是更换之后的代码:(复制粘贴即可)

using System;
using UnityEditor;
using UnityEngine;

namespace UnityStandardAssets.ImageEffects
{
    [CustomEditor(typeof(ColorCorrectionLookup))]
    class ColorCorrectionLookupEditor : Editor
    {
        SerializedObject serObj;

        void OnEnable()
        {
            serObj = new SerializedObject(target);
        }

        private Texture2D tempClutTex2D;


        public override void OnInspectorGUI()
        {
            serObj.Update();

            EditorGUILayout.LabelField("Converts textures into color lookup volumes (for grading)", EditorStyles.miniLabel);

            //EditorGUILayout.LabelField("Change Lookup Texture (LUT):");
            //EditorGUILayout.BeginHorizontal ();
            //Rect r = GUILayoutUtility.GetAspectRect(1.0ff);

            Rect r; Texture2D t;

            //EditorGUILayout.Space();
            tempClutTex2D = EditorGUILayout.ObjectField(" Based on", tempClutTex2D, typeof(Texture2D), false) as Texture2D;
            if (tempClutTex2D == null)
            {
                t = AssetDatabase.LoadMainAssetAtPath(((ColorCorrectionLookup)target).basedOnTempTex) as Texture2D;
                if (t) tempClutTex2D = t;
            }

            Texture2D tex = tempClutTex2D;

            if (tex && (target as ColorCorrectionLookup).basedOnTempTex != AssetDatabase.GetAssetPath(tex))
            {
                EditorGUILayout.Separator();
                if (!(target as ColorCorrectionLookup).ValidDimensions(tex))
                {
                    EditorGUILayout.HelpBox("Invalid texture dimensions!\nPick another texture or adjust dimension to e.g. 256x16.", MessageType.Warning);
                }
                else if (GUILayout.Button("Convert and Apply"))
                {
                    string path = AssetDatabase.GetAssetPath(tex);
                    TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
                    bool doImport = textureImporter.isReadable == false;
                    if (textureImporter.mipmapEnabled == true)
                    {
                        doImport = true;
                    }
                    // 修改:检查纹理压缩格式
                    if (textureImporter.textureCompression != TextureImporterCompression.Uncompressed)
                    {
                        doImport = true;
                    }

                    if (doImport)
                    {
                        textureImporter.isReadable = true;
                        textureImporter.mipmapEnabled = false;
                        // 修改:设置纹理压缩格式为无压缩
                        textureImporter.textureCompression = TextureImporterCompression.Uncompressed;
                        AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
                        //tex = AssetDatabase.LoadMainAssetAtPath(path);
                    }

                    (target as ColorCorrectionLookup).Convert(tex, path);
                }
            }

            if ((target as ColorCorrectionLookup).basedOnTempTex != "")
            {
                EditorGUILayout.HelpBox("Using " + (target as ColorCorrectionLookup).basedOnTempTex, MessageType.Info);
                t = AssetDatabase.LoadMainAssetAtPath(((ColorCorrectionLookup)target).basedOnTempTex) as Texture2D;
                if (t)
                {
                    r = GUILayoutUtility.GetLastRect();
                    r = GUILayoutUtility.GetRect(r.width, 20);
                    r.x += r.width * 0.05f / 2.0f;
                    r.width *= 0.95f;
                    GUI.DrawTexture(r, t);
                    GUILayoutUtility.GetRect(r.width, 4);
                }
            }

            //EditorGUILayout.EndHorizontal ();

            serObj.ApplyModifiedProperties();
        }
    }
}

十三.结语

        《Tank_Battle》不仅是一款具有娱乐性的游戏,也是一个优秀的 Unity 开发实践项目。对于初学者来说,它展示了如何利用 Unity 引擎进行场景搭建、对象控制、碰撞检测和特效实现等基础开发技术;对于有一定经验的开发者,也可以从中获取关于双人对战游戏设计和实现的思路和方法。同时,项目还提供了可能出现问题的解决方案,有助于开发者在遇到类似问题时快速解决。
 


http://www.niftyadmin.cn/n/5868223.html

相关文章

[关联字串]

关联字串 真题目录: 点击去查看 E 卷 100分题型 题目描述 给定两个字符串str1和str2,如果字符串str1中的字符,经过排列组合后的字符串中,只要有一个字符串是str2的子串,则认为str1是str2的关联子串。 若str1是str2的关联子串,请返回子串在str2的起始位置; 若不是关联子…

如何从头正确构建身份验证系统(针对 Laravel 初级开发者)

若你是一名 Laravel 开发新手&#xff0c;以下是我为你精心准备的指南&#xff1a; 第一步&#xff1a;深入了解 Laravel 的身份验证机制 首先&#xff0c;你需要从基础学起&#xff1a; 密码哈希&#xff1a;了解 Laravel 如何利用 bcrypt 算法对密码进行安全哈希处理。 会…

绩效管理与业务流程

绩效管理本质就是价值管理&#xff0c;或者说是能力管理&#xff0c;也就是通过一系列的科技手段去发现、证明一个人的能力和价值&#xff0c;然后给予科学、合理的利益分配。业务流程就是把企业的每一个零部件或者说齿轮都有效组合起来形成一个有机体为市场提供自己的独特价值…

如何通过云计算实现业务的持续创新?

云计算为企业提供了前所未有的灵活性、扩展性和创新机会&#xff0c;它已经不仅仅是一个技术工具&#xff0c;而是推动企业持续创新的关键驱动力。通过云计算&#xff0c;企业可以降低成本、加速开发周期、优化资源利用&#xff0c;并快速响应市场变化&#xff0c;从而实现业务…

夜莺监控 - 边缘告警引擎架构详解

前言 夜莺类似 Grafana 可以接入多个数据源&#xff0c;查询数据源的数据做告警和展示。但是有些数据源所在的机房和中心机房之间网络链路不好&#xff0c;如果由 n9e 进程去周期性查询数据并判定告警&#xff0c;那在网络链路抖动或拥塞的时候&#xff0c;告警就不稳定了。所…

OpenCV的形态学操作

在计算机视觉中&#xff0c;形态学操作是一种基于集合论的图像处理技术&#xff0c;主要用于分析和处理图像的形状特征。OpenCV 提供了 cv2.morphologyEx() 函数&#xff0c;用于执行多种高级形态学操作。 kernel np.ones((15, 15), np.uint8) 1. 开运算&#xff08;Opening&…

Vue进阶之AI智能助手项目(五)——ChatGPT的调用和开发

AI智能助手项目 前端页面Layout布局页面-viewssrc/views/chat/layout/Permission.vuesrc/views/chat/layout/sider/index.vuesrc/views/chat/layout/sider/List.vuesrc/views/chat/layout/sider/Footer.vueComponents 组件Header/index.vueMessage/index.vue前端页面 Layout布…

PC端-发票真伪查验系统-Node.js全国发票查询接口

在现代企业的财务管理中&#xff0c;发票真伪的验证至关重要。随着电子发票的普及&#xff0c;假发票问题日益严峻&#xff0c;如何高效、准确的对发票进行真伪查验&#xff0c;已经成为各类企业在日常运营中必须解决的关键问题。翔云发票查验接口做企业财务管理、税务合规的好…