笔者刚刚接触unity,跟着B站上的教程学着做了一个小项目,若有不足,请各位大佬多多指教。
1. 创建项目
视频教程网址:
https://www.bilibili.com/video/BV1V4411W787?p=6&spm_id_from=pageDriver
项目预览
素材搜集:Window-general-Assert store
导入后可在assert文件夹中查看
2. 角色移动
- 新建文件夹 scripts脚本
- 创建脚本文件PlayerControl.cs
可能出现的问题:unity没有补全功能
在unity中设置,edit-preference 默认vs再打开
public class PlayerController : MonoBehaviour
{
public float speed = 5f;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
float moveX = Input.GetAxisRaw("Horizontal");//control horizon move A:-1 D:1 else: 0
float moveY = Input.GetAxisRaw("Vertical");//control vertical move W:1 S:-1 else 0;
Vector2 position = transform.position;
position.x += moveX*speed*Time.deltaTime;
position.y += moveY * speed * Time.deltaTime;
transform.position = position;
}
}
- 将脚本拖给人物
3. tilemap绘制地图
- 左侧窗口右键创建 Grid-tilemap
- 创建tile文件夹,里面可以放瓦片
- 裁剪瓦片 右侧spliteditor
调整行与列,自动裁剪成n*m - window-2D-tilepalette
调色板中放入图片来作为地板
4. prefab丰富场景
- 将要加入的物体加入到prefab文件夹中,作为模板,方便复制粘贴
- 给物体添加刚体与碰撞
给人物添加刚体(rigid body) 与碰撞(Box colider)
给物体添加碰撞(Box Colider) - 设置人物重力为零,刚体中gravity设置 (bug 1: 人物不停下降,根本停不下来)
- 更改 edit-project Setting-graphics
调整mode为自定义(custom axis)
x,y,z 0 1 0
目的为图层的穿插更真实
4. 解决碰撞与抖动问题
- 解决碰撞旋转 (bug 2:爱的魔力转圈圈)
人物-刚体-freeze rotation z - 解决抖动问题: (bug 3: 不断抽搐)
public class PlayerController : MonoBehaviour
{
public float speed = 5f;
Rigidbody2D rBody;//刚体组件
// Start is called before the first frame update
void Start()
{
rBody=GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
float moveX = Input.GetAxisRaw("Horizontal");//control horizon move A:-1 D:1 else: 0
float moveY = Input.GetAxisRaw("Vertical");//control vertical move W:1 S:-1 else 0;
Vector2 position = rBody.position;
position.x += moveX*speed*Time.fixedDeltaTime;
position.y += moveY * speed * Time.fixedDeltaTime;
rBody.MovePosition(position);
}
}
修改为刚体的位置,并且使用fixedDeltaTime替换DeltaTime
4.相机跟随
添加相机
import package cinemaMachine
GameObject-cinemaMachine-2dCamera
将人物添加到相机的follow中
地板碰撞
添加tilemapcollider
将除了水面以外 的瓦片 collidertype 都设为none
镜头边界
左侧创建新建空物体,取名为cameraConfirmer
为这个物体添加组件:Polygon Collider
编辑边界
在原相机中extension tools添加camera collider,把自己写的放进去。
confiner中点击istrigger,避免将人物弹出**(bug 4: 两者不兼容)**
采集道具:
- 图片调整合适大小拖入map,添加碰撞,打开触发器。
- 编辑脚本
在playcontroller中添加
public class PlayerController : MonoBehaviour
{
public float speed = 5f;
private float MaxHealth = 5;
private float MinHealth = 0;
private float currHealth ;
public float getMaxHealth()
{
return MaxHealth;
}
public float getMinHealth()
{
return MinHealth;
}
public float getCurrHealth()
{
return currHealth;
}
Rigidbody2D rBody;//刚体组件
// Start is called before the first frame update
void Start()
{
this.currHealth = 2;
rBody=GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
float moveX = Input.GetAxisRaw("Horizontal");//control horizon move A:-1 D:1 else: 0
float moveY = Input.GetAxisRaw("Vertical");//control vertical move W:1 S:-1 else 0;
Vector2 position = rBody.position;
position.x += moveX * speed * Time.fixedDeltaTime;
position.y += moveY * speed * Time.fixedDeltaTime;
rBody.MovePosition(position);
}
public void changeHealth(float add)
{
currHealth=Mathf.Clamp(add+currHealth, 0, MaxHealth);//玩家生命值约束在0与Maxhealth之间
}
}
新建脚本 collectable.cs
private void OnTriggerEnter2D(Collider2D other)
{
PlayerController player = other.GetComponent<PlayerController>();
if (player != null)
{
Debug.Log("玩家碰到草莓");
Debug.Log(player.getCurrHealth() + "/" + 5);
if (player.getCurrHealth() < player.getMaxHealth())
{
player.changeHealth(2);
Debug.Log(player.getCurrHealth() + "/" + 5);
Destroy(this.gameObject);//拾取草莓后摧毁草莓
}
}
}
受到伤害
- 创建脚本,类似于collectable
private void OnTriggerStay2D(Collider2D other)
{
PlayerController player = other.GetComponent<PlayerController>();
if (player != null)
{
Debug.Log("玩家碰到陷阱");
Debug.Log(player.getCurrHealth() + "/" + 5);
if (player.getCurrHealth() > 0)
{
player.changeHealth(-1);
Debug.Log(player.getCurrHealth() + "/" + 5);
}
}
}//注意方法为OnTriggerStay2D,可实现持续收到伤害
- 在Playercontroller中改动实现间隔受到伤害
private float invincibleTimer;
private float invincibleTime=2f ;
private bool isinvincible=false;
//
if (isinvincible)
{
invincibleTimer -= Time.deltaTime;
if(invincibleTimer < 0)
isinvincible=false;//两秒以后取消无敌状态
}
//在update函数中
public void changeHealth(float add)
{
if(add<0)
{
if (isinvincible)
{
return;
}
isinvincible = true;
invincibleTimer = invincibleTime;
}
currHealth=Mathf.Clamp(add+currHealth, 0, MaxHealth);//玩家生命值约束在0与Maxhealth之间
}
//修改changeHealth函数
-
实现人物站在陷阱里也能收到伤害
人物刚体选择neverawake -
伤害区域的扩充
选中伤害图片-drawmode-tiled平铺
添加敌人
1,给敌人加上刚体与碰撞,重力设为0;
2,编辑脚本EnemyController
public class EnemyController : MonoBehaviour
{
public float changeDirectionTime = 2f;//改变方向的时间
public float changeTimer;//改变方向的计时器
public float speed = 3f;
public bool isVertical;//是否垂直方向移动
Rigidbody2D rbody;
private Vector2 moveDirection;//移动方向
// Start is called before the first frame update
void Start()
{
rbody= GetComponent<Rigidbody2D>();
moveDirection=isVertical ? Vector2.up : Vector2.right;//如果垂直移动,向上,否则向右
changeTimer = changeDirectionTime;
}
// Update is called once per frame
void Update()
{
changeTimer -= Time.deltaTime;
if (changeTimer < 0)
{
moveDirection *= -1;
changeTimer = changeDirectionTime;
}//计时器使来回运动
Vector2 position=rbody.position;
position.x+= moveDirection.x*Time.deltaTime;
position.y += moveDirection.y * Time.deltaTime;
rbody.MovePosition(position);
}
/// <summary>
/// 与玩家的碰撞检测
/// </summary>
/// <param name="collision"></param>
private void OnCollisionEnter2D(Collision2D collision)//碰撞体检测,不会穿过,要求两刚体之间
{
PlayerController pc = collision.gameObject.GetComponent<PlayerController>();
if (pc != null)
{
pc.changeHealth(-1);
}
}
}
添加动画(难点!!!)
物品动画
- 选中需要添加动画的物体, 点击windows-animation 新建一个动画 新建在item文件夹下创建
- add Property – scale 控制缩放 在每一帧设置不同的大小
- 另外所有要加动画的物体点击修改过的物体 prefebs-apply all
敌人动画
- 选中预支动作图片,将其批量选中walkdown拖动到robot下,会打开资源管理器,选择新建enemy文件夹存放,保存为walkdown.anim 。
- 分别如此创建向上下左右的动画,特例右和左是对称的,所以在向右animation中 add property – sprite renderer.FlipX,在x轴上旋转。
之后再添加fixed动画 - 动画的应用,创建混合树,在animator界面-右键-create state-new blend tree
blendType:2D Simple
下方加号:add motion field,把上下左右的动作拖入,分别设置positionX,Y的变化(初始化为moveX,moveY)
返回base layer 使entry指向blend tree
左侧参数添加 moveX,moveY 以及trigger型的fixed,利用右键blend tree实现transition的连接,点击这根转换线,将transition的参数设置为trigger型的变量fixed - 修改动画代码:
public class EnemyController : MonoBehaviour
{
public float changeDirectionTime = 2f;//改变方向的时间
public float changeTimer;//改变方向的计时器
public float speed = 3f;
public bool isVertical;//是否垂直方向移动
Rigidbody2D rbody;
private Vector2 moveDirection;//移动方向
// Start is called before the first frame update
private Animator animator;
void Start()
{
rbody= GetComponent<Rigidbody2D>();
animator= GetComponent<Animator>();
moveDirection=isVertical ? Vector2.up : Vector2.right;//如果垂直移动,向上,否则向右
changeTimer = changeDirectionTime;
}
// Update is called once per frame
void Update()
{
changeTimer -= Time.deltaTime;
if (changeTimer < 0)
{
moveDirection *= -1;
changeTimer = changeDirectionTime;
}//计时器使来回运动
Vector2 position=rbody.position;
position.x+= moveDirection.x*Time.deltaTime;
position.y += moveDirection.y * Time.deltaTime;
rbody.MovePosition(position);
animator.SetFloat("moveX",moveDirection.x);
animator.SetFloat("moveY", moveDirection.y);
}
/// <summary>
/// 与玩家的碰撞检测
/// </summary>
/// <param name="collision"></param>
private void OnCollisionEnter2D(Collision2D collision)//碰撞体检测,不会穿过,要求两刚体之间
{
PlayerController pc = collision.gameObject.GetComponent<PlayerController>();
if (pc != null)
{
pc.changeHealth(-1);
}
}
}
人物动画
- 人物组件添加animator,添加控制 Ruby(官方自带,也可以用Blend tree自行完成)
- 修改代码:
public class PlayerController : MonoBehaviour
{
public float speed = 5f;
private float MaxHealth = 5;
private float MinHealth = 0;
private float currHealth ;
private float invincibleTimer;
private float invincibleTime=2f ;
private bool isinvincible=false;
private Animator animator;//动画机
private Vector2 lookDirection = new Vector2(1, 0);//初始朝向默认为向右
public float getMaxHealth()
{
return MaxHealth;
}
public float getMinHealth()
{
return MinHealth;
}
public float getCurrHealth()
{
return currHealth;
}
Rigidbody2D rBody;//刚体组件
// Start is called before the first frame update
void Start()
{
this.currHealth = 2;
invincibleTimer = 0;
rBody=GetComponent<Rigidbody2D>();
animator = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
float moveX = Input.GetAxisRaw("Horizontal");//control horizon move A:-1 D:1 else: 0
float moveY = Input.GetAxisRaw("Vertical");//control vertical move W:1 S:-1 else 0;
//获取朝向:
Vector2 moveVector= new Vector2(moveX, moveY);
if(moveX!=0 || moveY != 0)//至少一个不为零(按下了键盘)
{
lookDirection = moveVector;
}
animator.SetFloat("Look X",moveVector.x);
animator.SetFloat("Look Y", moveVector.y);
animator.SetFloat("Speed", moveVector.magnitude);//向量的模
//控制移动
Vector2 position = rBody.position;
//position.x += moveX*speed*Time.fixedDeltaTime;
//position.y += moveY * speed * Time.fixedDeltaTime;
position += moveVector*speed*Time.deltaTime;
rBody.MovePosition(position);
//控制无敌时间
if (isinvincible)
{
invincibleTimer -= Time.deltaTime;
if(invincibleTimer < 0)
isinvincible=false;//两秒以后取消无敌状态
}
}
public void changeHealth(float add)
{
if(add<0)
{
if (isinvincible)
{
return;
}
isinvincible = true;
invincibleTimer = invincibleTime;
}
currHealth=Mathf.Clamp(add+currHealth, 0, MaxHealth);//玩家生命值约束在0与Maxhealth之间
Debug.Log("当前生命值" + currHealth);
}
}
添加子弹
-
将子弹拖入场景,加上刚体与碰撞,重力设为零,不加z旋转,不勾选 isTrigger
-
添加脚本 bulletController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletController : MonoBehaviour
{
private Rigidbody2D rb;
private void Awake()
{
rb = GetComponent<Rigidbody2D>();//优先于start
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void Move(Vector2 moveDirection, float moveForce)
//发射方法
{
rb.AddForce(moveDirection*moveForce);
}
}
-
修改图层与碰撞
右上角layer图层添加 player与bullet层
edit-preference-2D physical 修改碰撞:player与bullet之间,bullet与bullet之间禁止碰撞
添加enemy逻辑
public void Fixed()
{
rbody.simulated = false;//禁用物理模拟
animator.SetTrigger("fixed");//播放被修复的动画
isfixed = true;
}
plyercontroller 添加发射逻辑与动画
if (Input.GetKeyDown(KeyCode.J))
{
animator.SetTrigger("Launch");
GameObject bullet = Instantiate(bulletPrefab, rBody.position+Vector2.up *0.4f ,Quaternion.identity);
//默认方向,四元数,参数分别为预制体类别,产生出来的位置,旋转的初始角度
BulletController bc=bullet.GetComponent<BulletController>();
if (bc != null)
{
bc.Move(lookDirection, 300);
}
}
添加特效:可以自学粒子相关
烟雾特效
- 右键左侧物品栏,新建特效particle System
https://www.bilibili.com/video/BV1V4411W787?p=15
人物血条
- 添加ui
右侧列表-右键新建ui-image
将要添加的图片加进去
调整缩放模式convas-scale with screen size
调整血条:filed, horizontal
- 编辑ui事件,在血量变化同时ui界面血量条随即变化
npc对话框,相应事件
子弹数量ui
链接如下,需要补充。
B站教程
感谢大家阅读!!!