目录
总结:如果需要玩家控制,就用FixedUpdate,如果需要自己动,且是每帧都要动,就用Update
总结:普通的碰撞体不会受OnTriggerEnter函数的影响,只有触发器碰撞体才行
一、建立游戏环境
1、创建一个新场景
先用URP模板创建一个项目
把模板文件都丢进新建文件夹Template里方便管理
创建一个新场景
接下来保存一下这个新场景(类似于保存一个项目吧)
2、创建一个平面
三种方法创建
可以点菜单栏的GameObject
也可以点左上角的加号
还可以直接右键
之后
3、平面的缩放
两种方法
将平面设置成以下数值
注意,这只是平面,Y轴方向无体积,所以拉Y轴数值没什么用
4、创建玩家对象
5、调整光照
注意这个Rotationd的用法,假如我改X的数值,是Y和Z所在平面旋转
6、用材质添加颜色
二、玩家的移动
1、给小球添加物理模拟组件
2、安装输入系统Input System
这样玩家才能用键盘或者手柄控制小球
3、给小球添加玩家控制组件
4、创建一个脚本
5、写移动脚本
知识点:
1. Update函数每帧调用一次,所以在渲染一帧前调用
2. FixedUpdate函数每隔间隔时间调用,所以用来处理物理模型
这个头文件就相当于把脚本和组件连接起来了,玩家将按键信息传给组件,组件又将信息传给脚本(现在脚本可以访问组件里的按键信息)
组件传的信息是InputValue类型的,变量movementvalue将会储存x与y轴的上的输入数据,这个数据可以储存为一个Vector2变量
用OnMove函数获取小球的移动输入,并储存为Vector2变量
Start函数下的代码会在脚本激活的头一帧被调用,一般而言就是游戏的第一帧
三、镜头移动
1、设定摄像机位置
摄像机对象会继承玩家对象的Transform变换,当玩家移动时,摄像机也会随机移动
2、写一个镜头移动脚本
这样,在每帧被绘制之前,摄像机会和玩家对象对齐,这次对齐的仅有坐标而没有旋转角
但是,我们无法控制Update函数的运行顺序,Update可能会先于其他修改位置的脚本或Unity系统运行,比如Physics物理模拟系统,所以,可以用LateUpdate,它只会在其他Update(包括其他脚本)完成后执行,这下摄像机位置只会在玩家移动后才被更改
总结:摄像机的镜头Update应该是最后才执行的
四、建立游戏场地
1、做一堵墙
无论创建什么,之后都要记得命名和重置,还要加点材质
2、复制多堵墙
方块自带盒型碰撞组件,组件会对带有Rigidbody和Collider组件的对象作出反应
五、制作可收集物
1、创建一个收集物
(创建后就要命名,重置,材质三步)
2、给收集物写一个旋转脚本
总结:如果需要玩家控制,就用FixedUpdate,如果需要自己动,且是每帧都要动,就用Update
总结:引用的组件 名称用小写,变量的名称首字母要大写
3、用预制件批量生产收集物
一个预制件是游戏对象或其分支的模板和蓝图,当一个对象变为预制件,它就可以应用于项目的任何场景中(相当于把对象变成一个文件了,可以随便放),单个道具的实例或预制件上的修改会应用到所有游戏中的其他实例上(改一个就可以改全部)
六、检测碰撞
1、禁用接触到的碰撞物体
只要用了OnTriggerEnter,就能识别脚本绑定对象(玩家)所接触的一切的碰撞物体(墙,地板,方块),它们统称为other
2、为收集物预制件贴标签
但是现在会把墙,地板,方块全部禁用掉,我们只想禁用方块,那么就需要给方块加上一些标签,如果小球碰到带有收集物标签的物体,那么禁用代码才会被调用(if函数)
标签可以通过比对字符串来识别出对象类别
注意名字要区分大小写,因编辑器标签名字和脚本标签名字必须一模一样,包括大小写
3、只禁用带收集物标签的物体![](https://i-blog.csdnimg.cn/direct/a02745fba460430c988e2a71865350fb.png)
别忘了加一个花括号
4、将收集物碰撞体设为触发器
一定要退出运行模式再改设置,因为运行模式下更改设置不会保存
总结:普通的碰撞体不会受OnTriggerEnter函数的影响,只有触发器碰撞体才行
5、为收集物添加刚体(运行更加高效)
每个带有刚体的游戏对象都会被视为动态的,Unity不会将它们作为静态或静止对象处理,为了提升运行速度,这里的收集物不应该是静态的,因为Unity会花更多的时间来计算其物理,在每帧上计算方块的旋转(相当于把每帧的静态方块拼在一起合成一个动态的图像,但是动态物体就不用,能节省很多计算量),所以可以给收集物对象添加刚体而让它们变为动态的
关掉重力后,虽然小球少了一个向下的力了,不会再往下掉了,但是不排除以后会有其他力继续拽着方块走,然后各种穿模
电梯,传送平台,触发器等等都可以设置成运动刚体,经由变换来运动
总结:带有刚体的动态碰撞体才能移动,普通刚体受物理作用影响而移动,运动刚体受变换影响而移动。创建一个新对象时,思考它是动态的还是静态的,如果是动态(需要移动),就加一个刚体组件,再思考是普通刚体还是运动刚体,如果不需要玩家动,自己就能动,就换成运动刚体
七、显示得分与游戏文本
1、记录收集物数量
2、创建UI文本
UI对象用的是Rect Transform而非普通的Transform,所以界面和其他对象的不一样
如果感觉文本离画布边缘太近,可以自行调整
3、显示得分
EventSystem有一个报错,这是因为组件仅兼容旧版输入管理器,需要我们将组件替换成新的输入系统组件,这样就能在随后为项目添加其他UI互动了
4、创建游戏结束文本
当玩家收集完所有方块时,则提示“You Win!”
八、构建游戏
游戏默认会全屏运行,但我们这个游戏很小,也没有菜单,所以就设置成窗口运行
接下来是打包
然后把这个Builds文件夹压缩成压缩包就可以发给别人了
九、代码部分
1、玩家代码
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerController : MonoBehaviour
{
public float speed = 0;
public TextMeshProUGUI countText;
public GameObject winTextObject;
private Rigidbody rb;
private float movementX;
private float movementY;
private int count;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
count = 0;
SetCountText();
winTextObject.SetActive(false);
}
void OnMove(InputValue movementValue)
{
Vector2 movementVector = movementValue.Get<Vector2>();
movementX = movementVector.x;
movementY = movementVector.y;
}
void SetCountText()
{
countText.text = "Count:" + count.ToString();
if(count>=12)
{
winTextObject.SetActive(true);
}
}
private void FixedUpdate()
{
Vector3 movement = new Vector3(movementX, 0.0f, movementY);
rb.AddForce(movement * speed);
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("PickUp"))
{
other.gameObject.SetActive(false);
count = count + 1;
}
SetCountText();
}
}
2、摄像机代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraController : MonoBehaviour
{
public GameObject player;
private Vector3 offset;
// Start is called before the first frame update
void Start()
{
offset = transform.position - player.transform.position;
}
// Update is called once per frame
void LateUpdate()
{
transform.position = offset + player.transform.position;
}
}
3、方块旋转代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rotator : MonoBehaviour
{
// Update is called once per frame
void Update()
{
transform.Rotate(new Vector3(15, 30, 45)*Time.deltaTime);
}
}