25729人加入学习
(149人评价)
Unity2D 初级案例 - 坦克大战(Unity2017.1)

制作于2017年12月23日

价格 免费

fixedUpdate 能解决一些刚体运动碰撞的 

[展开全文]

 

切换图片的渲染和显示

拿到组件的引用,改一下属性

 

 

 

 

图片沿着Z轴做旋转

 

 

 

 

 

[展开全文]

 

新建文件夹:Scripts 存放脚本

找到组件,添加脚本

1. Add Compoment

 

打开脚本编辑:

1. ⚙️/edit

2. 双击小方框

3. 双击文件

 

[展开全文]

我用的是2022.3.3的版本,调整map大小的时候是可以全选所有的地图素材,一次性调整的

[展开全文]

屏幕:5:4;摄像机:size8.5

2d游戏:图片格式Sprite;

单张图片:Sprite Model调为Single;图集调为Multiply。

图集切割:Sprite Editor

 

 

 

[展开全文]

 private void FixedUpdate()
    {
        float v = Input.GetAxisRaw("Vertical");
        float h = Input.GetAxisRaw("Horizontal");

        if (Mathf.Abs(v) > 0.0f)
        {
            transform.Translate(Vector3.up * v * moveSpeed * Time.fixedDeltaTime, Space.World);
            if (v < 0)
            {
                sr.sprite = tankSprite[2];
            }
            else if (v > 0)
            {
                sr.sprite = tankSprite[0];
            }
        }
        else if (Mathf.Abs(h) > 0.0f)
        {
            transform.Translate(Vector3.right * h * moveSpeed * Time.fixedDeltaTime, Space.World);
            if (h < 0)
            {
                sr.sprite = tankSprite[3];
            }
            else if (h > 0)
            {
                sr.sprite = tankSprite[1];
            }
        }
    }

[展开全文]

1.代码提示问题

打开preferences→extemal tools

2.在window中可以打开自己想要的面板

可以自由拖放自己的面板,然后在layout保存自己喜欢的布局

一般用tall,然后调整一下project

3.后期导入的模型跟音效都会保存在Assets里

4.创建项目后项目名称带*,说明项目没有保存

5.快捷键

复制 :ctrl+D

聚焦物体 :F

环绕查看:Alt+鼠标左键

步移工具:ctrl

修改名称:F2

注释:Ctrl+K

取消注释:Ctrl+U

自动吸附顶点:V

在视野的模式下:按Q键向下,E向上,W向前

6.旋转

Local围绕自身轴旋转

Gobal围绕空间轴旋转

(Local自身坐标系,Gobal全局坐标系)

Transform中的Rotation表示旋转度数,Scale表示缩放

7.pivot建模原点;center中心

8.使用步移工具,必须要切换到Global状态

9.每个游戏角色都有自己名字,F2修改名称

10.smoothness光滑程度

11.mesh网格;Mesh Runderer在网格上渲染

12.建模一般建两种,一种精细化用来渲染,一种是简模,用来做碰撞检测

13.发生碰撞效果,添加“刚体组件Rigidbody”

script创建脚本(脚本也是一种组件,可以通过创建/删除组件来控制)

14.C#script中,如"public class Player"中的Player,后期就算修改要记得跟运动物体保持一致

15.注释:Ctrl+K、Ctrl+C

取消注释:Ctrl+K、Ctrl+U

16.如何使刚体组件与具体的一个运动物体连接,有两种方法:

1.在代码中更改,=在算法中相当于赋值:如rd=GetComponent<什么类型的组件如Rigidbody>();刚体是模拟物理效果,我们要给他施加力才能运作起来,在update中给施加持续的力,如:

rd.AddForce(力的大小跟方向如Vector3.right);

2.拖拽的方式 :将Player拖拽到组件rd的位置,但拖拽容易出现引用丢失的情况。如果有两个刚体组件的话,会以代码指定的为准。

17.代码中的public/private:组件中的属性可以是公开的,也可以是私有的,一般只把必要的属性设置为公有的。如果设置为私有的就没办法通过拖拽设置值了。

18.向量:forward、Left、back、right

19.监听键盘按下:float h = Input输入,GetAxis得到轴("Horizontal"水平轴) ,h是一个小数的意思。点击播放按钮,小球落下,点击图形界面,按键A,看到Consale里从0→-1的变化。Debug.Log(h);→rd.Addforce(new Vector3(h,0,0))l;注:vector中的h如果赋不上值,可以复制前面的内容。键的控制快捷键可以通过Project Settings→Axes中修改;player的运动速度可以通过rd.AddForce(new Vector3(h, 0, v)*1);*1代表速度

20.控制相机跟随小球移动:在Camer中添加脚本→计算两个位置的差→Vector3 offset(偏移)=transform.position(自身的位置)-(小球的位置)playerTransform.position;

小球的位置怎样获得?输入属性:public Transform playerTransform;然后回到界面,打开Camer,把player拖拽到Camer中的Script

设置主相机的位置:transform.position=playerTransform.position+offset;(注:offset下方有波浪线,因为offset是在start方法里面定义的,所以在update里面是没有办法使用的,解决方法:输入private Vector3 offset;

21:添加墙:创建Cube→positon设置Y:0.5

Scale中的Z为20。创建材质、附材质。复制墙Ctrl+D,将四周都围上墙。

22.预制体:地面等都属于环境:创建一个空物体命名为Env,将环境相关的物体都拖到Env下面。创建预制体:预制体相当于一个模板,在Assets下创建一个模板文件Prefabs,如:创建一个方体作为Food,将方体放入Prefabs文件中作为一个预制体,可以直接Ctrl+D复制,例如Food(1)作为Food的孩子,修改预制体Food其他孩子也会变化,如果修改Food(1)想让Food发生变化的话,将Food中的Overrides修改为Apply all,这样母亲Food连同其他孩子都会被修改。(注:当一个物体被旋转后再使用移动工具,坐标轴会跟物体坐标轴一致,这时可以打开Gobal模式,就可以跟世界坐标轴一致了。)

Prefab 预制体

Instance 实例

Class 类

Instance (对象-Object)实例

23.控制小球旋转:给Food预制体创建一个脚本→tansform.Rotate旋转(Vector3.up);

24.查文档:在Help:Unity Manual,Scripting API→Class类(文档当作字典查阅的)(UnityEngine→Class)

25.碰撞检测:在player写一个系统事件(如start、Update都是系统事件),写oncollisionEnter,会自动给出系统事件,输出(Debug.Log("发生碰撞了")),碰撞一共有三个阶段,oncollisionEnter,oncollisionExit,

oncollisionStay,如果物体碰撞之后player会把food吃掉,就不需要执行Exit、Stay方法了。

区分Food的方法:名字/标签,但是名字跟标签都可以重复,给它添加标签Add Tags,修改Prefabs里的标签,所有的Food都有标签了。

collision.gameObject.tag(碰撞到游戏物体的标签)

条件编程语句:用来判断碰撞物体条件if(collision.gameObject.tag=="Food")→

Destroy(collision.gameObject);

(解释:是先通过collision再到gameObject,然后再找到标签的)(注意:赋值时用的是==而不是=)

标签获得:可以通过游戏物体获得标签,也可以通过组件获得标签。即一个为collision.gameObject.tag

collision.collision.tag

26.通过触发检测,吃东西:触发检测是可以不发生碰撞效果就可以吃东西,只要进入触发检测的范围内。

创建一个Cube,然后给一个标签test,将Cube里Box Collder中的is Trigger勾选。触发检测也有三个过程,OnTriggerEnter;OnTriggerExit;OnTriggerStay;

If(other.tag=="Food")

Destory(other.gameObject);

然后将Food的碰撞器修改为触发器。

27.通过UI显示得分:

1.首先给定义一个整数变量:public int score=0;(int:整数 score:变量)

score++:++是自增运算符。

2.创建UI:Button按钮,Text文本,Image图片;创建一个Text,切换到2D模式,双击Canvas(画布,Evensystem事件系统),调整text组件(1.调整字在框中的位置及,居中 2.调整小框在大框中的位置,按着Alt+右上角的键,可以自动对齐右上角3.调整字体大小.)

3.将数字计算(score)与UI关联:

引入UI的命名空间:using UnityEngine. UI

定义一个组件:public text scoreText;

将Text拖拽到Score Text下面,这样Player就可以有访问UI组件的权限了。

分数发生变化时要更新UI显示:

scoreText.text="sdfaesd";(字符串的含义是,把sdfaesd给UI,即UI显示sdfaesd,如果是显示分数的话,应改为scoreText="分数"+score;

28.显示游戏胜利:创建一个UI,text,(为防止后期text混乱,给每个text重新命名,ScoreText,WinText),把wintext禁掉,等游戏胜利了再激活wintext。

1.首先给一个游戏物体的引用:

public GameObject wintext;

在if(other.tag=="Food")下面给If(score==12)

{ winText.SetActive(true);(解释:true/false,SetActive激活,是否激活)

29.游戏打包:File→BuildSetting→addopensence

然后在unity Hub中安装程序用来打开游戏

如果是PC端导出,选PC,点击Build,创建

默认是全屏状态下运行,如果改成窗口状态下运行:Player Setting→resolution(分辨率)

→window(窗口模式)→创建

 

 

案例2:打砖块

1.创建地面、墙体:创建MineScen→创建Plane→reset使地面位于中心部分→改名ground→创建Material→附材质;建墙体Cube→Y0.5重命名Brick→创建Prefab文件夹→将Brick拖到Prefab里→复制墙体→步移(按着Ctrl移动,就会根据步长,移动步长是可以设置的,在左上角Scene窗口里)→选择复制6排高→选中墙块 右键创建Empty parent

2.控制相机移动:给相机加一个Script→

movement→transform组件用来控制自身的位置、旋转、大小→Transform.translate(变换的方法)(Vector3.Left);

(注意:我们在使用某个组件的时候,是先使用某个组件如Transform,再使用这个组件上的某种方法如Translate的)

用wasd来控制相机方法:首先给相机一个水平轴:float h=Input.GetAxis("Horizontal");

transform.Translate(new Vector3(h,0,0));

相机移动过快解决:update的更新次数→创建一个Script命名为LearnUpdate→Debug.Log(Time.deltatime);

Debug.Log(1/Time.deltaTime);

(FPS:每秒可以执行多少帧,成为延迟)

∵运动距离=速度*时间

∴Transform.Translate(new Vector3(h,0,0)*Time.deltaTime);(这里的Vector3代表着在x轴运行的一个速度)

如果想以两倍的速度移动相机:

Public int speed=1;

∴Transform.Translate(new Vector3(h,0,0)*speed*Time.deltaTime)

再给相机一个y轴方向的移动

float v=Input.GetAxis("Vertical");

(注:写代码习惯性将方法首字母大写,属性首字母小写)

Update更新次数是不确定的,但FixedUpdate更新次数是固定的,也可以设置,在ProjectSetting中可以设置执行间隔时间

3.发射小球:创建一个球体,Bullet,将其放在Prefab里→在Camer身上添加一个新的脚本Shoot:定义一个Prefab:

Public gameObject bulletPrefab

(可以是场景中的某个物体,也可以是个Prefab)

//根据Prefab创建实例=实例化Prefab

GameObject.Instantiate实例化(bulletPrefab.transform.position,

transform.rotation);(position,rotation代表相机的位置)

当我们按下鼠标左键的时候再去实例化,怎样检测按键?如下:

跟按键有关的都是通过Input输入的

Input输入.GetMouseButtonDown(0)

(0代表左键,1代表右键)

if(Input.GetMouseButtonDown(0))

{GameObject.Instantiate(bulletPrefab.transform.position,

transform.rotation)};

如果想让物体有物理效果要添加刚体组件

在bullet下添加Rigidbody,方法:

1.获取刚体组件2.给它施加力

if(Input.GetMouseButtonDown(0))

{GameObject bullet=GameObject.Instantiate(bulletPrefab.transform.position,

transform.rotation)};

rigidbodyrd=bullet.getcomponent<Rigidbody>();

rd.AddForce(Vector3.forward*100);

(100代表100N的力)

(GetMouseButtonDown,代表鼠标按下)

∵当通过力的方式给它速度,没办法直观看到给的力的大小及转化成的速度

∴这里可以通过给速度的方式

rd.velocity=Vector3.forward*30;

(forward向前的力)

给墙一个物理效果rigidbody

 

Unity中资源的导入方式:

Asset Store资源商店里有→下载好资源→拖拽到unity中/右键import package

unity自带的资源导入:打开unity安装文件→unityDownloadAssitant→standard Assets→安装在unity的目录下

添加窗口:在菜单栏的空白处右键,addTab

Layer可以控制层:锁定、隐藏

maximize;最大化游戏窗口

聚焦某个物体:1.双击物体2.按F键

Transform组件:position.rotation.scale;reset重置值

V工具可以吸附顶点:方便顶点对齐

预制体属性:创建一个Cube→拖到Assest里变成一个预制体→Ctrl+D复制两个,修改其中一个的属性,其余两个不变→如果想让其变回最原始状态可以选择reset,变成预制体的状态可以选revert;

 

案例3:古迹探险

古迹地形设计:创建Terrain:地形设计

Terrain Height:最大高度

创建一个Terrain的文件夹,将TerrainDate放入

笔刷:1.刷到最大高度就不会再变大了

2.地形还原:按shift+笔刷,Opacity可以控制笔刷的程度。

3.添加贴图:在笔刷选项下→rise or lower Terrain→paint Texture

4.导入树:Tree→speedtree→可以导入树

5.刚体组件:选择相机→Align with view相机跟当前视野保持一致→小球→rigidbody

Drag:摩擦力(既会增加下落的阻力,也会增加在地面的阻力)

Angular Drag:旋转时候的摩擦力

Use Gravite:是否使用重力

Freeze Position:冻结坐标轴

Freeze Rotation:冻结旋转轴

Mass:物体的质量:影响物体运动的惯性

 

碰撞器:

Terrain Collider:如果地形碰撞器容易出现错误,我们给添加一个Box Collider,调节至跟地面贴合

Mash Collider:会比较费面,耗性能,碰撞更精准,根据面的多少来看

Mash Renderer:针对不太精准的碰撞,且面比较多的情况,这个碰撞器就可以

 

碰撞检测:如果选sphere,就是检测在shpere身上发生的碰撞,如果选cube就是检测在cube身上发生的碰撞

创建Script→指定给Sphere

oncollsionEnter、OncollsionExit、OncollsionStay

(注意:两个物体发生碰撞的前提是有碰撞器collider,其中一个或两个都有刚体rigidbody)

经过测试: OnCollisionEnter方法被触发要符合以下条件

1 碰撞双方必须是碰撞体

2 碰撞的主动方必须是刚体,注意我的用词是主动方,而不是被动方

3 刚体不能勾选IsKinematic

4 碰撞体不能够勾选IsTigger

 

触发检测:小球可以穿过检测物体,不发生碰撞。创建Cube中BoxCollider中的Is Trigger,取消勾选Mesh Render或者将其移除

OnTriggerEnter、OnTriggerExit、OnTriggerStay

print("OnTriggerEnter:"+other);

(other会直接输出触发器的名字)

print("OnTriggerEnter:"+other.name);

(.name可以获取触发器所在游戏物体的名字)

print("OnTriggerEnter:"+other.tag);

(.tag可以获取触发器所在游戏物体的标签)

 

要注意ontriggerEnter方法写的位置,不要写在大括号内部

 

灯光:右键创建PointLight、arealight区域光

火焰:windows→Lighting→Settings

烘焙:将含有地形的Env文件选择Static,Light也勾选Static,Terrain也勾选

再去lighting中,点击General Lighting生成光照贴图Generate Light生成光照贴图→禁用灯光检查是否烘焙上(场景变得无灯光效果就是没有烘焙上)→没有烘焙上,选择灯光→Mode选择Baked→再把灯光启用→再进行烘焙→再把灯光禁用后依旧有灯光的效果

在火光的灯光效果中:Mode选择Mixed,因为火光需要一闪一闪的效果,因此需要计算

 

火光:(在这之前创建灯光时将灯光放入Perfab文件,方便后期Apply all)创建Particle systerm→调整速度→粒子样式open Editor→可以选择导入的fire

shape:调整火焰形状

Start Lifetime:火焰大小控制

Start speed:可以调整为Random between two contents两个数值控制速度

size overLifetime:修改火焰大小渐变

Rate over Time:火焰数量

Radius:火焰聚拢

粒子样式:找到火焰贴图所在的位置→创建Material→命名为flame→如下图创建好材质后→导入Rander中作为贴图

 

(1)Renderer模块

设置其Material,将火焰粒子贴图放入其中。

(2)Texture Sheet Animation模块

由于火焰贴图共有4帧,因此需要将其水平和垂直方向的平铺数量划分成2,设置Tiles的x和y为2。

(3)shape模块

radius设置为0,使得粒子产生的位置固定。

Force over Lifetime模块

粒子受到的力作用,起初可以将三个方向都设置为0。之后当模拟火焰受到风的影响而偏移时,可以调整x的值。

Color over Lifetime模块

设置粒子随时间而产生的颜色变化。上面的标记控制透明度的变化,下面的标记控制颜色的变化,火焰的底部偏蓝色,从底部往上,火焰会由蓝变橙红,并且粒子生命将要结束时,将会逐渐变透明。

(4)main(主模块)的设置

Duration:发射器喷粒子的时间长度

Looping:重复持续发射粒子

Start Lifetime:单个粒子的生命周期,这里设置为0.3到0.5之间的随机值。

Start Speed:粒子的初始速度。

Start Size:粒子大小,由于火焰会忽大忽小,因此设置为0.6到1之间的随机值。

Start Rotation:粒子旋转的弧度

Gravity Modifier:粒子所受重力,由于火焰是向上窜的,因此设置为-1.

 

灯光闪烁效果:选中火焰,window→Animation→保存至Animation→选中Animation中的Bonfire→AddProperty→Intensity

火苗播放的很同步调整:Animation→Transform→Position

 

导航系统:window中nevigation →注意要将跟地形有关的物体static勾选,才能烘焙

Agent Rudius:调整可视范围

想穿过草,要将草的nevigation static关掉。然后回到nevigation重新烘焙

如果不想让人物行走在石头上面:可以选中石头。再点击一下会生成Mesh render→再回到nevigation 里Object→nevigation area→notwalkable→重新烘焙

选择人物→添加Nev Mesh Agent组件→redius、Height

给英雄添加导航组件:创建脚本hero→

if(Input.GetMouseButtonDown(0)){}

当鼠标按下左键的时候触发射线检测

{ Ray ray=Camera.main.ScreenPointToRay

(Input.mousePosition);

RaycastHit hit;

if(Physics.Raycast(ray,out hit))

{ //print(hit.point);

agent.SetDestination(nit.point);

}

}

获取射线=主相机里有ScreenPointToRay用来把屏幕坐标转化为射线,屏幕坐标获取=鼠标位置获取(Input.mousePosition);RaycastHit

hit用来保存碰撞信息;Physics.Raycast(ray,out hit);

Physics里有Raycast射线检测,Raycast里有ray射线,out hit参数;如果发生碰撞,可以检测到点击地面的哪个位置,print(hit .point);输出点的位置

要人物沿着鼠标点击的路线运动:

即Nev mesh agent沿着鼠标运动

首先引入Nev mesh命名空间Using UnityEngine.AI,然后就可以定义Nev mesh agent组件public NavMeshAgent agent,agent可以直接通过拖拽的方式赋值,

agent.SetDestination(hit.point);

有了agent 就可以通过agent里的Destination用来设置目标位置,这时候就不需要前面的print(hit.point)了

设置hero转向:在Nav Mesh Agent里设置Augular speed

 

 

控制摄像机的跟随:首先确定相机跟hero之间的偏移→用update使hero与hero保持偏移

创建相机脚本:首先定义一个Transform组件,public Transform hero,用Transform找到hero的位置,取得位置偏移

private Vector3 offset;

offset=transform.position-hero.positon;

偏移=当前位置-hero的位置

transform.position=offset+hero.positon;

相机跟主角的偏移=

 

控制英雄的动画播放:创建一个动画状态机,在Animation里创建Animator Controller,用来控制hero的动画播放,赋值,打开Animator,

-idle是默认状态,(控制hero的原理是通过控制其速度来控制状态),创建float参数,命名为speed,将idle与walk状态链接,箭头指向walk,设置其最大速度Greater,speed>0时,由idle切换至walk,设置其最小速度less,speed<0.1时,由walk切换至idle,

由walk切换至run时,当最大速度>2时,由run切换至walk,当最小速度<2时;这时这个动画还不太发生变化,需要给其赋值:

去代码中,获取animator 组件,public Animator anim;赋值给hero中的anim组件

anim.setfloat("speed",agent.velocity,magnitude);

状态机里 参数需要animator来设置,velocity速度,magnitude速度大小

 

案例4

通过案例驱动学习UGUI:

canvas中的pixcl periect:使字更柔和,如果想让字体清晰,勾选pixcl periect

图片空间:创建3D场景→保存至scene里,命名为image.字体文件命名为Text

导入图片(新建文件夹Texture),修改TextureType→Sprite(2D)→应用

应用图片:右键UI→image:

→Source Image选择需要的图片

Raycast Target:检测鼠标跟图面发不发生关系,比如图面上发生点击等事件,默认是勾选的,通过碰撞检测检测到的

等比例放大:按shift放大;setNative Size恢复原本大小

Button组件里:Hightlight Color高亮状态;

Target Graphic:修改某个图片的状态,也可以修改别的状态,只要把别的物体的Button组件赋值过来就好

Pressed Color:鼠标按下时的状态;

select color:被选中时的状态

Interactable:没办法触发点击

创建一个自己喜欢样式的Button:创建image→创建一个喜欢的样式→给一个Button组件→如果没有文字的话,可以创建子文件text

给任何一个游戏物体创建Button Manager脚本如main Camera:

添加一个方法:

public void onButtonClick()

{print("ButtonClick");}

可以添加一个带参数的方法:

public void OnButtonClick(int i)

{print("ButtonClick+"+i)}

把Button节点拖动到onClick列表的“None”那里(注意一定是节点,不能是脚本!),下拉箭头ButtonManager→OnButtonClick(int)

 

 

锚点:创建一个新场景,命名为Anchor,创建一个UI,button,一般情况下右上角的UI以右上角为锚点,左上角的UI以左上角为锚点,这样在不同屏幕里显示,UI不会被移除屏幕

在Rect Transform里修改锚点的位置

pivot:中心点

 

登录UI开发:

创建场景,005-Login,Canvas→UI→image

如果使图片跟屏幕分辨率保持一致,跟着画布发生变化,在Rect Transform里修改锚点的位置,按Alt选最右下角的模式,使图片与其附体保持一致→用户名、密码Text,向右对齐→创建输入框,右键InputField→修改其Content Type→Password输入密码时变为密码格式***

→Placeholder是提示信息,当真正输入时Placeholder被禁用Text是输入的信息

登录按钮设置:右键UI→Button→登录→在mineCamera 添加脚本“LoginController”→定义一个方法“public void OnloginButtonClick(){ }→找到LoginButton的OnClick,给runtime赋mineCamera的值,方法选择LogController→

OnLoginButtonClick()

输入框设置:选usernameInput选项→创建

InputField 组件,在LoginController中定义方法:先给一个命名空间using UnityEngine.UI;

public InputField usernameInput;public InputField passwordInput;→赋值→

public void OnloginButtonClick”{

string username=usernameInput.text;

string password=passwordInput.text;

if(username=="admin"&&password=="admin"){//场景跳转 sceneManager.LoadScene("001-text");

场景跳转到001-text场景

}

→选择主界面file箭头BuildSetting→把当前场景及001场景拖拽进去(这样才可以跳转成功)

(注意:在创建每个Text时记得命名

用户名:usenameLable密码:passwordLable

输入用户名:UsenameInput密码:PasswordInput登录:LoginButton)

(注意:有些名称需要引用命名空间“Using UnityEngine....”可以从左边错误提示下拉按钮找到)

密码填错时警告信息:创建一个Text→messageable(可以把整个物体禁掉,或只把Text组件禁掉)→在LoginController中添加方法“public void onLoginButtonClick()

else{messageText.text="用户名或密码错误,请重新输入”;messageText.enable=true;}

警告信息只显示一分钟:添加一个携程IEnumerator HideMessage(){yield return new WaidForSeconds(1);messageText.enable=false;}→回到else调用这个携程:StartCoroutine(HideMessage);

 

UI中的控件:

单选和多选按钮:

Toggle:单选按钮

男女勾选多选案例:设置两个Toggle,建立一个空物体GameObject,将两个物体放入空物体中,方便分组,在空物体中建立一个组件Toggle Group组件,将GameObject的值赋分别给男女Toggle赋值,

这时候这两个为一组,这是两个选项只有一个is on是勾选上的

 

Slider:控制血量、音量大小:

File就是控制进度时需要显示的部分

Image Type:是单独控制图片分割的形式的,使用其中的Solid功能时需要给图片指定一个边框:

给图片指定边框:导入图片,点击图片把Texture type更改为sprite(2D),通过Sprite Editor给图片编辑一个边框,如何下载Sprite Editor:在paceage那里右键,打开view in package Manager:unity registy是unity网络包还未导入可以下载安装,选2Dsprite;可以形成一个边框,运行时尺寸会有变化,因为image type中的slide选项,在变换图片时,不会成比例变换,上边框只会左右拉伸,左右边框只会上下拉伸。(例:一个有四角边框的图片,如果给其设置边框,再变化边框时,边框线不会发生变化)。在image type中的simple选项中放大缩小边框会被等比例拉伸的,边框线也会等比例拉伸,影响像素。

Flie Center:显示中间部分

image type中的Tild选项:平铺

(导入图片→创建一个Button→给按钮图片样式source image中导入→调节图片的边框范围)

 

UI中的控件:

Scrolbar:拖拽条,进度条。direction可以调节滚动条方向

Dropdown:下拉菜单

在Canvas中有panel选项,可以将控件放在同一个面板中,如果不想保留面板图片,可以勾掉image就只保留面板线框了

 

 

开发开始界面:创建新场景:009-StartScene→image→填充方式“四角填充”

声音按钮:创建UI,(导入的UI的TextureType改为Sprite2D&UI)image找到对应图片,命名为"BGM",按shift缩放到适当的大小;在此image下再创建一个image,找到相应的按钮图片SetNativeSize然后再等比例放缩,在此之下再创建一个imgae,放一个声音按钮,重复以上步骤。把以上命名为“AudioButton”,并添加一个Button组件,

添加叶子:image,shift+Alt等比例放大,命名为leaf

复制AudioButton,再做一个EmailButton

Play按钮:创建Text,调整Font Style,

Effect Distance阴影影响面积

创建shadow组建,命名为StartButton,

 

创建新场景,命名为010-PlayScene:

人物状态,Text:“LV50”,将此image命名为“Head”

进度条(血量值):Slider(如果不需要跟用户交互的话可以将Interactable勾选掉),在设计中往往需要的是Slider组件

创建一个image,在此之下再创建一个image用来做血量值背景“BlackBgm”,再创建一个血量值image命名为"GreenBar",添加一个Slider组件,将GreenBar添加到Fill Rect里,在GreenBar中,ImageType修改为Fill,Fill method修改为horizontal,然后就可以回到上层image中通过修改vaule来修改血量值了。Disable降低透明度变为正常状态。

给进度条加上小叶子、创建按钮+图标

设置图标,命名为“SettingButton”创建一个Button组件,复制一个做对话框图标,命名为

“DialogButton”

技能图标开发:命名为“skill”外边框,setnegativesize,在此image下面添加一个技能image,再复制一个作为这个图片的遮罩:将这图片变为黑色或者深灰色,命名为coldMask,Fill Amount可以调节。叶子、Text,给字体添加边框,即添加Outline组件,调整边框的粗细,边框的颜色。添加一个Button:

开发机能冷却效果:

添加脚本组件命名为“Skillitem":首先提供一个方法:public void OnskillClick()当技能被点击时会触发的

技能需要冷却时间的,定义一个:

public float coldTime=2;

计时器:用来计时冷却

private float timer=0;

是否处于冷却期

private bool isColding=false;

private Image coldMask;

在start中

{coldMask=transform.Find("coldMask").

GetComponent<Image>();

if (isColding){

timer+=Time.deltaTime;

coldMask.fillAmount=(coldTime-timer)/coldTime;剩余时间比例

if(timer>coldTime){isColding=false;FillAmount=0;timer=0;

}}

public void OnskillClick()

{if(isColding=false){isColding=true;timer=0;

coldMask.fillAmount=1;}

写好以上代码后,在skill的Button组件中,添加一个点击事件,OnClick→SkillitemOnSk

 

 

(注:在C#中,Tab引入系统自动给的提示命令)

 

按下键的时候也可以触发技能:见笔记

 

Panel面板:

Image→设置边框→命名为Panel→标题背景板:添加文字→字描边

面板内部边框:选择边框,调整大小→如果外边框变动,内边框也跟着变动→将内边框的锚点放在面板的四个角上,标题的锚点,设置在panel上面。

 

决策场景:012-Character-Knapskack

Image→重复上次Panel动作→关闭按钮“CloseButton”→导入线框→头像“Head”→人物属性:Text、Image等几个创建成一个Empty

Parent(注:全部选中子物体,让它们跟父物体的中心保持一致,这样比较好调整)

背包:Image→命名为“Knapsack"→标签的Image需要一个父image 一个子image ,标签命名为”item“→父物体上添加一个控件Toggle,复制三个,将选择变为单选:

将这三个标签再创建一个父类,命名为“itemGroup”,将三个标签先移出,调整itemGroup的位置高宽设置为100,按住ALT

拉伸一下→在itemGroup下添加组件ToggleGroup,将这三个指定给TG ,并把最后一个设置为选中状态,ison勾选,还有文字未选中情况下的遮罩状态

创建Panel1、Panel2、3

找到item1,的on vaule changed,将Panel1赋予,setactive,当选择Panel1的时候会激活,选panel的时候会禁用→设置初始状态,两个装备隐藏

物品栏:在panel3 下创建一个空物体→在其下面创建组建GridLayout Group(网格布局),命名为Grid。创建一个image,命名“item”,找一个背景,在其下方再创建一个image,找一个图标。如果直接将item拖到Grid下方,复制多了就会发现每个图标的距离很小,因此,创建一个空物体,item放在空物体下面,中心点一致。这时可以将其放在Grid下方,设置其间距

→设置分页,给每一个Panel都设置一个分页

,然后给每一个Panel做item

在UI文件里,创建一个prefabs,吧charactorPanel拖拽进去,

 

关卡选择:

013-LevelSelect:首先创建画布Canvas,给一个背景,再把上面的charactorPanel的预制体放入此文件夹,在charactorPanel右键prefab里有Unpack,这样就可以断开连接了,删除用不到的东西。TitleBgm,CloseButton,创建边框,做item(有上锁效果),Grid,排序方式,StartAxis,Vertical,两行两行填充。

(注意:Grid原点放在中心的时候,修改wide时向两边延伸,Grid原点放在右边,再修改wide,是从右向左延伸)

做一个锁着的状态“Levelitem-lock"放进预制体里,修改后Apply all。这样需要上锁效果的,直接拖拽预制体填充就好。

列表滚动:创建一个空物体,命名为ScrollRect,其大小跟每一页的大小是一致的如800*400,将Grid放置其下,新增组件ScrollRect,将Grid给Content、viewport赋值,∵滚动是受超出范围来,只有有图片的时候才能检测到滚动,∴在ScrollRect里添加一个image,image里有一个Raycast Padding是可以检测的。如果垂直滑动跟水平滑动都可以的话,把Scroll里的Vertical关闭,把超出界限的部分隐藏,创建一个Mask,要将之前image的颜色透明度调回正常,之前是透明,将show Mask Graphic取消掉

分页滚动:在ScrollRect里添加脚本“LevelScrollRectangle”,脚本思路见笔记

(注意:以上内容都做完但是还是滚动不了的原因,出现在刚开始的步骤,Grid的宽度设置600*350,这是一个页面的宽度,需要4个页面,2400*350,要注意Grid的中心点调至左边,调中心点还有看工具栏的选项是中心点还是轴心点,选中心点然后才能移动中心点的位置,后面移动不了,都是因为这个步骤错了)

 

按页滚动:创建空物体“PageButton”→在其下创建image“圆点”再其下创建image“叶子”→在image添加一个Toggle,给其赋值,这样勾选ison,可以使叶子显示或隐藏,Ctrl+D,复制,这样四个圆点就做好了,在PageButton设置ToggleGroup,赋值,这样点击圆点,叶子就能显示了,回到脚本监听OnvauleChange方法,见笔记

 

任务栏:创建场景014-Taxtlist→将ChatacterPanel→先取消连接,然后删除不用的内容,可以按住Alt调整画布的宽度,创建任务条,给其边框使其拖拽不变形,创建“完成”按钮,给这个Text一个outline,给这个按钮添加一个Button,命名为DownButton,将以上内容放在一个文件下面,命名为“item”

任务列表布局:创建一个空物体“Verticallist”,在其身上创建一个VerticalLayoutGroup,将item做成预制体,然后放入垂直列表内,然后可以复制多份,然后设置其滚动区域image、Mask、然后上下滑动(取消horizontal勾选)设置面板,

声音大小滑动器:新场景015-Settings,Text声音→slider→设置slider背景、音量条、拖动按钮样式, 开关样式:创建image做打开模式下的开关“on",再创建关闭样式“off",添加组件“Toggle”,由于Toggle只能显示图片的开关,不能连接到游戏上,所以创建代码:"Mytoggle",代码逻辑见笔记

登陆面板:016-Login:按住ALT键对称缩放,用户名、密码,对于密码,content type要改为password。

 

 

坦克大战:

1.场景搭建:main Camera:设置Camera的大小为8.5,颜色调为黑色,注:信息显示面板Console,在window里打开,可以看报错信息,

切割素材:用sprite editor→Grid by Cell Size(以固定大小来切割),调整到合适的尺寸,切好图集→保存场景→创建文件夹Scence

创建玩家,玩家比对着Cube更改尺寸,创建一个文件夹,Prefabs,将玩家创建为预制体。再做墙,障碍向右拖拽Barrier,再做Grass,再做一个Heart(老窝),动画命名为“Bom”,将以上的预制体全部拖到预制体文件夹里,在预制体文件夹里创建一个Effect文件夹,存放特效。把爆炸特效的控制器放在一个文件夹里

玩家出生时会有一个屏障效果:shield,

玩家游戏时动作转换:创建一个脚本:见笔记,创建数组后,将需要用的图片赋予public sprite

碰撞检测:创建2D碰撞器,(在预制体上添加碰撞器是无法添加到同样的克隆体上的,因此)

给player添加一个刚体组件,(注:是rigibody2D),运行起来会发现player很快运动,这时将Gravity Scale重力,调整为0,给map里的其他物体添加碰撞器,草坪不需要交互,所以取消Grass的碰撞器

player运动时图片方向斜着,解决方法:在

rigidbody→constrains→Freeze Rotation Z

player在碰撞到物体时会有点鬼畜,解决办法:用代码:生命周期函数,见笔记

控制player的运动方向:脚本笔记

让特效覆盖在玩家的上面:精灵渲染器Sprite Renderer里,有sorting layer,order layer,sorting layer是层级,order in layer层级顺序。如,将order in layer设置为1,层级越大越后渲染,也就是说,最不想让谁被覆盖掉,就把谁设置为大,这样可以最后被渲染。

将子弹拖入场景,拖入坦克文件夹,order in Layer,设置为1

写玩家的攻击方法:写脚本 →将子弹的预制体赋给Tank里的Bullect Prefab,然后在脚本里调用一下方法

子弹旋转角度:见脚本

攻击CD的加入,触发器与碰撞器:在子弹预制体身上加一个脚本,“Bullect”,在脚本中控制方向

触发检测:给子弹添加一个触发器,(因为子弹不需要碰撞效果,所以只要一个触发器)BoxCollider2D,两方都有碰撞器组件,一方的is Trigger勾选上的,最好碰撞的那方有刚体,即在Bullet里加上Rigidbody2D。在脚本里给子弹加上触发检测的方法→给物体添加标签,add Tag,然后在脚本里编写触发检测的语句。→→做个空气墙:在界面复制一个墙体,把其中的渲染器删掉,即把sprite Renderer删掉,命名为“Air Barrier”,然后把它拖入Map文件夹做预制体

玩家无敌方法与死亡方法:见脚本。

死亡方法:坦克在break之前需要爆炸特效及生命力减1等效果。需要用到爆炸特效→产生爆炸特效:给爆炸特效一个脚本:“Explosion”,然后在player调用一下Explosion脚本至explosion Prefab.

无敌方法:被保护状态,如果玩家处于被保护状态,让玩家不会销毁。见脚本。然后,由于不能一直让特效产生,所以,找到玩家,把“shield”放在玩家下方然后reset一下,这样当玩家处于无敌状态时显示一下,之后不是无敌状态时就关闭。见脚本修改。

判断玩家子弹还是敌人子弹:见脚本

注意:写完脚本,记得修改frefabs上的标签,改成各自的名称。

如果子弹射击没有反应:有可能是Order in Layer的渲染层级跟被碰撞物体的渲染层级不在一个层级,所以要想使两个物体发生作用,要将其渲染层级调至一起。

区分玩家子弹与敌人子弹:将玩家子弹重命名,为playerBullect,并勾选 is player Bullect,然后再复制一个playerBullect,命名为EnemyBullect,勾选掉is player Bullect.

敌人制作:复制玩家,然后把无敌状态删除。更换图标,删除玩家脚本,重新添加新的脚本。

将玩家脚本复制到敌人脚本里:见脚本

老窝的爆炸效果:找到heart脚本,

爆炸重生脚本:Bron脚本,private woid BornTank()→调用一个延时特效Invoke("BornTank",0.8f)→引用一下玩家→延时销毁

设置第二个玩家:SmallEnemy→把图片的切换添加一下,精灵数组里添加→敌人不需要保护状态,把Explosion跟Bullet添加好就好。smallEnemy的移动速度改为4,打开Born脚本:见笔记。记得把enemy挂载的脚本改为enemy的脚本。

 

初始化地图工具: 1.测试场景大小:选中一个敌人,拖动敌人拖到左下角的位置,例坐标为-10,-8,0,拖到中间位置为0,-8,0,所以地图的大小大约是长20,宽18。所以创建一个GameObject→命名为MapCreation→新添加脚本“MapCreation”,见脚本。→赋值后只留一个小坦克来测试。→实例化老家,在(0,-8,0)的位置来实例化→见脚本→墙中间有缝隙,可能是墙有点小,可以缩放为3.2

 

游戏运行起来后,在界面会有罗列,我们想让罗列的内容在一个物体下,如MapCreation,面板看起来不会很混乱,因此添加一个方法→在MapCreation脚本里,见脚本→让它随机产生敌人,而且不在一个地方产生两个物体,见脚本。

 

实例化地图:见脚本。把草的渲染参数调为1。

玩家管理:设置一个GameObject→然后更名为“PlayerManager”→增加一个脚本

UI设计:给main Camera设置为灰色,拖黑色背景,放大至跟原先屏幕即5:4的大小差不多,然后把main Camera调整为16:10→BackGround的渲染层级设为-2

Img-PlayerScore:找到相应的图片,调整至居右对其,设置Text,字写20,字体颜色为白色,

在PlayerManager的脚本里设置一下,见脚本

  1. 游戏结束UI:创建一个UI,找到相应图片,然后去脚本修改。
[展开全文]
  • create folder Scripts
  • 选中左侧player,添加一个脚本,
  • add component,
  • 输入player,new cript,等下方圆圈转完
  • 双击player,打开vs

 

 

 

[展开全文]

 

 

  • born

选中四张图片,拉到hierachy,形成born动画

 

  • 动画重命名:

borncontroller

  • born拉到animation文件夹
  • borncontroller拉到animatorcontroller文件夹

 prefabs文件夹下,新建文件夹,Map

预制体拉到map

新建文件夹effects,将born拖进去

explore1和explore2拖进hierachy,形成爆炸特效

命名为Explosion

大小不再调整,拖到effects文件夹

出生的时候,无敌效果,shield

大小3.3.3拖到effects文件夹

 

河流,map3,map4,river

大小3.3.3拖到effects文件夹

左边Gimes,去掉camera

 

动画命名

river rivercontroller

explosion explosioncontroller

shield shieldcontroller

分别拖入animation和animatorcontroller文件夹

[展开全文]

1

display1 5:4

2

main camera 

blackground black

inspecter size 8.5

3

graphics texture type

2d and ui

4

player1

sprite editor

5

save game

[展开全文]

画布的自适应 canvas sacle with screen size

[展开全文]

把场景UI都放在一个空物体下即可通过该物体控制整体移动

作业一    移动界面动画

作业二    双人游戏

Editor project settings Input 右键复制

作业三    红色坦克奖励  时钟 停止运动

在敌人脚本中设置一个bool值 return

堡垒  wall 变障碍  把wall销毁重新创建一个障碍

设置一个计时器

炸弹 消灭场内所有敌人 定义一个列表 把敌人的gameobject 存进去 再遍历删除

帽子 重新激活无敌特效与bool值

作业四 给tank履带做动画 加一个动画控制器Animator

[展开全文]

音效的添加

在MapCreation里把那些Awake里的封装成一个方法 InitMap

注意方法的执行语句不用再加private什么的了

在Recover里加上返回主界面的功能 在PlayerManager里

using UnityEngine.SceneManagement;

先新建一个方法

private void ReturnToTheMainMenu()

场景 0 语句

在recover来个Invoke延时执行

Die audio

先拿引用

public AudioClip dieAudio;

在heart die方法

AudioSource.PlayClipAtPoint(dieAudio,transform.position);

直接在当前位置播放

Explosion 音效

直接在Explosion上面挂Audio Source 组件

play on Awake 打勾 游戏物体一出来就播放

把音效拖上去

打到障碍的声音

给障碍加个脚本

拿引用public AudioClip hitAudio;

碰撞检测语法 封装一个方法 

把所有碰撞检测的放在子弹里

在bullet里调用一下就可以了 发送碰撞消息

start 音效 用组件放在Background上面

fire 音效 放在player子弹身上

引擎音效

给玩家添加AudioSource组件 用语法控制其附带音效变化

在player脚本中拿下组件

piblic AudioSource moveAudio;

数组拿音效素材

public AudioClip[] tankAudio;

直接把组件拖到public

再拖素材

检测 如果v跟h的值是0 则播放idle,不是0则是driving 做一个判断  怎样简洁

音效 = 数组中的一个 再播放

else 没有大于

在上面的isDefeat方法里加入returnToMenu

heart失败的情况

 

 

 

[展开全文]

第一个场景的制作

ctrls保存当前场景,ctrlN新建一个场景 Main

摄像机调黑

新建一个image battlecity

再新建一个 image 坦克指针

按shift等比缩放

再建两个空物体

把Pos1放在指针下边并reset一下 让其位置重合 大小手动调整重合

把Pos2删了直接从Pos1复制

按下WS时应该移动 并

再在option上加上同名脚本

   int变量=0

private int choice = 1;

位置引用

public Transform posOne;

public Transform posTwo;

update里

if监听键盘

choice = 1;

位置变换语法

transform.position = posOne.position;

游戏场景using

场景变换语法

再把引用拖一下

再拖一下游戏场景

file-Build Settings

直接拖到框框里 一个0一个1

[展开全文]

把Heart方法里加入失败方法

再玩家管理中加入 public bool isDefeat;

再在Heart中Die方法调用该单例

PlayerManager.Instance.isDefeat = true;

再在玩家脚本中FixedUpdate

里加入判断 return 

把攻击的CD移进来

UI的制作

再Game中调成16:10

将摄像机背景颜色设置为灰色

再从资源中拿入黑色图 调成5:4 这样就可以在旁边灰色区域放UI了

从资源中拿入图片

右键新建UI image

在image中调成居右对齐 自适应

在两个图片中直接设置text子对象 20 居中

再在PlayerManager中导入UI的命名空间

using UnityEngine.UI;

拿一下引用 公共变量法

public TMP_Text playerScoreText;

public TMP_Text  playerLifeValueText;

在其update中更新 转一下字符串

再拖一下引用 记得用TMPro哦

新建一个image gameover

在recover方法中补充完整,生命值小于等于0

isDefeat = true;

再在上面拿一下引用

public GameObject isDefeatUI;

update里

if判断一下啊 记得return

拖一下引用

[展开全文]

将敌人v初始值是负的,一出生往下走

将旋转计时器一开始设为0

玩家状态的管理

创建一个空 PlayerManager

再挂一个同名脚本

属性值 生命值 得再写单例 静态

右键圈住 按住ctrl不松 然后按住re

游戏一开始便在Awake方法里

Instance = this;

玩家复活的方法

public bool isDead;

private void Recover()

先判断一下生命值 小于0与--,生命值未归为0,则重新出生

再拿一下born的引用 公共变量法

再在else中 用一个GameObject'变量接收一下born(共用的born)

再拿脚本 将createPlayer = true;

isDead = false;

再在update里判断一下如果死亡 则调用Recover方法

再在玩家的死亡方法里  加上调用

PlayerManager.Intance.isDead = true;

得分

再在敌人的死亡方法里

PlayerManager.Instance.PlayerScore++;

 

 

[展开全文]

敌人AI的优化

敌人相撞在一起转向

定义一个2D的触发检测的方法

如果标签是 注意引号 让转向时间计时器瞬间等于4

 

[展开全文]

初始化地图的其他游戏物体

坑:只有tank等移动的要加刚体,他们的碰撞体不用钩Is Trigger

实例化地图

for循环 每个产生20个 让墙多一点

初始化玩家(Born)

拿到变量   得到Born组件 将其createPlayer设置为true,

敌人 一开始三个,之后随机在这三个位置产生

bool值默认false,故不用再设

随机产生敌人的方法

用随机数0~2(0,3)

给一个空间坐标变量

再做判断if位置

再在原来位置加上InvokeRepeating(延时调用的方法)参数:(方法名,延时时间,每隔多久)

 

[展开全文]

编写产生随机位置的方法

一个列表 装已经有东西的位置

private List<Vector3> itemPositionList=new List<Vector3>();

所以在CreateItem方法里要加入,每生成一个要把坐标add进list

itemPositionList.Add(createPosition);

产生随机位置的方法 while循环

//外围一圈不产生游戏物体 x=+-8.3的两列,y=+-4.5这两行

用V3类型的变量来接受随机的位置 随机数的方法

-7.3f,8.3f,-3.5f,4.5f

如果这个位置是空的(再在外面写一个方法)

再把这个V3变量return回去

有东西了则继续while循环

方法//判断列表中是否有这个位置

private bool (判断方法 bool类型返回值) HasThePosition(Vector3 createPos)

z再用一个for循环遍历列表 itemPositionList.Count表示列表长度

if =则return ture 不然则return flase

再在上面调用一下这个方法

if(!HasThePosition(createPosition))  z这个就是ture or flase

不是则返回位置(成功通过查重检测)把这个位置拿了出来

易错别把true写错了

//实例化外围空气墙 y=+-5.5f,x从+-9.3

几个for循环 用来限制移动范围包括子弹

for(float i=-9.3f; i<10.3f;i++)

(i,5.5f,0)

for(float i=-9.3f; i<10.3f;i++)(上下两行)

(i,-5.5f,0)

for(float i=-5.5f; i<6.5f;i++)

(-9.3f,i,0)

for(float i=-5.5f; i<6.5f;i++)

(-9.3f,i,0)

[展开全文]