-------小基原创,转载请给我一个面子

  主角都能移动了,那不得做点什么伸张正义,守护世界和平的事嘛,拿起家伙biu~biu~biu~

首先得做一个好人和一个坏人

老规矩,Canvas下创建两个Image,一个叫做player,一个叫做enemy1好了

 

一个红色,一个蓝色(自古红蓝出CP,不好意思,走错片场了●﹏●)

新知识:要加BoxCollider2D

子弹打到别人,其实是碰撞检测的过程

一种是根据位置坐标,判断子弹有没有打中,另一种是使用物理碰撞系统(小基这里使用后者)

两个物体物理碰撞检测条件

1.物体A,物体B,必须都有Collider

2.运动的一方要有刚体(本例对应的是子弹,后面介绍)

最终效果是这样的。

下面开始制作子弹

Canvas下创建一个Image,修改名字叫做Bullet,使用自带的圆形图片凑合用吧

同样添加BoxCollider2D,最后切记添加RigidBody,不然没法碰撞成功

额外提一句,重力调成0吧,不然子弹抛物线飞=_=

现在看起来是这样的。

接下来想想怎么让子弹动起来,上代码(创建一个脚本Bullet),给那个子弹挂上

 using System.Collections;
using System.Collections.Generic;
using UnityEngine; public class Bullet : MonoBehaviour { public float speed = ;
public Vector2 dir = new Vector2(, );
public float lifeTime = 5f; public float damage = ;
// Use this for initialization
void Start () {
DestroySelf(lifeTime);
} // Update is called once per frame
void Update () {
this.transform.Translate(dir * speed * Time.deltaTime, Space.World);
} void DestroySelf(float time)
{
Destroy(this.gameObject, time);
} void OnTriggerEnter2D(Collider2D other)
{
//Debug.Log("enter");
if (other.gameObject.tag == "Enemy")
{
Destroy(this.gameObject);
}
}
}

start()里面调用DestroySelf(),意思是子弹存活lifeTime 时间后,自动消失。不然“让子弹飞”飞到啥时候是个头啊,飞出屏幕外就没有意义了。存活时间可以自己控制

Update()方法里面,每帧都在按照指定的dir方向移动,你想让子弹怎么飞,朝哪飞,就自行设计吧,螺旋升天都没有问题~

OnTriggerEnter2D这个方法是Unity自带的,用处就是当进入碰撞盒Collider时调用,小基的内容是销毁自己,不是~意思是销毁掉子弹

这里加了一个if的判断,判断tag是不是Enemy,毕竟打到友军可是会被喷猪队友的

Tag一般默认是Untagged的,点击一下后有弹出AddTag这个选项,然后

点击加号,可以自己创建需要的tag,这样可以给不同类别的物体做区分。

下面考虑怎么让主角射出子弹,这个就需要代码控制了

 using System.Collections;
using System.Collections.Generic;
using UnityEngine; public class MyInput2 : MonoBehaviour {
//移动方向枚举
enum MoveDir
{
None = , //不动
Up = , //上8
Down = -, //下2
Left = , //左4
Right = -, //右6
UL = , //左上7
UR = -, //右上9
DL = , //左下1
DR = -, //右下3
} //输入按键常量(之后走配置)
const KeyCode INPUT_UP = KeyCode.W;
const KeyCode INPUT_DOWN = KeyCode.S;
const KeyCode INPUT_LEFT = KeyCode.A;
const KeyCode INPUT_RIGHT = KeyCode.D; //默认移动方向
private MoveDir moveDir = MoveDir.None;
//按压值
private int moveDirValue = ;
//按压记录
private bool isUpPress = false;
private bool isDownPress = false;
private bool isLeftPress = false;
private bool isRightPress = false; //是否可以移动
private bool canMove = true;
//右移动
private Vector3 MOVE_RIGHT = new Vector3(, , );
//上移动
private Vector3 MOVE_UP = new Vector3(, , ); //外部调控速度
public float speed = 2f;
//移动速度向量
private Vector3 move_speed_dir = Vector3.zero;
//移动距离
private Vector3 move_dis = Vector3.zero; //控制目标
public Transform target; //鼠标按下时的坐标
private Vector3 mouseStartPos = Vector3.zero;
//鼠标是否按下
private bool isMousePress = false;
//鼠标枚举
const KeyCode INPUT_MOUSE = KeyCode.Mouse0;
//鼠标拖动范围
const float MOUSE_RADIUS = ;
//鼠标移动向量
private Vector3 mouseDir = Vector3.zero;
//鼠标速度衰减
private float mouseSpeedRate = ;
//鼠标速度
public float mouseSpeed = ;
//摇杆底
public RectTransform joyStickDown;
//摇杆顶
public RectTransform joyStickUp;
//摄像机
public Camera camera; //子弹prefab
GameObject prefabBullet;
//是否开火
bool isFire = false;
// Use this for initialization
void Start () {
prefabBullet = (GameObject)Resources.Load("Prefab/Bullet");
//Debug.Log("bullet:" + prefabBullet);
} // Update is called once per frame
void Update () {
CheckInputKey();
CheckMoveDir();
CheckMouseDir();
} void FixedUpdate()
{
CheckMove();
CheckFire();
} //检测输入按键
void CheckInputKey()
{
//检测单一输入
foreach (KeyCode kcode in System.Enum.GetValues(typeof(KeyCode)))
{
if (Input.GetKeyDown(kcode))
{
//Debug.Log("Single KeyCode Down: " + kcode);
ChangeKeyPressState(kcode, true);
} if (Input.GetKeyUp(kcode))
{
//Debug.Log("Single KeyCode Up: " + kcode);
ChangeKeyPressState(kcode, false);
}
}
} //记录按键的按压状态
void ChangeKeyPressState(KeyCode keyCode, bool isPress)
{
switch(keyCode)
{
case INPUT_UP:
isUpPress = isPress;
break;
case INPUT_DOWN:
isDownPress = isPress;
break;
case INPUT_LEFT:
isLeftPress = isPress;
break;
case INPUT_RIGHT:
isRightPress = isPress;
break;
case INPUT_MOUSE:
MouseStateChange(isPress);
break;
case INPUT_FIRE:
isFire = isPress;
break;
}
} //鼠标按键输入
void MouseStateChange(bool isPress)
{
isMousePress = isPress;
mouseStartPos = isPress ? Input.mousePosition : Vector3.zero;
joyStickDown.gameObject.SetActive(isPress);
joyStickDown.position = camera.ScreenToWorldPoint(mouseStartPos);
} //鼠标移动
void CheckMouseDir()
{
if(isMousePress)
{
mouseDir = Input.mousePosition - mouseStartPos;
mouseSpeedRate = Mathf.Min(mouseDir.magnitude / MOUSE_RADIUS, );
move_dis = mouseSpeed * mouseSpeedRate * Time.deltaTime * mouseDir.normalized;
target.position += move_dis;
joyStickUp.localPosition = mouseDir.normalized * mouseSpeedRate * MOUSE_RADIUS;
}
} //确定移动方向
void CheckMoveDir()
{
moveDirValue = ;
//确定方向
if(isUpPress)
{
moveDirValue += (int)MoveDir.Up;
}
if (isDownPress)
{
moveDirValue += (int)MoveDir.Down;
}
if (isLeftPress)
{
moveDirValue += (int)MoveDir.Left;
}
if (isRightPress)
{
moveDirValue += (int)MoveDir.Right;
}
} //检测是否可以移动
void CheckMove()
{
//某些情况下可能禁止移动,例如暂停,播放CG等
if(canMove && moveDirValue != (int)MoveDir.None)
{
PlayerMove(target, speed);
}
} //移动
void PlayerMove(Transform target, float speed)
{
move_dis = speed * Time.deltaTime * GetSpeedDir();
target.position += move_dis;
} //速度向量
Vector3 GetSpeedDir()
{
switch(moveDirValue)
{
case (int)MoveDir.Up:
move_speed_dir = MOVE_UP;
break;
case (int)MoveDir.Down:
move_speed_dir = -MOVE_UP;
break;
case (int)MoveDir.Left:
move_speed_dir = -MOVE_RIGHT;
break;
case (int)MoveDir.Right:
move_speed_dir = MOVE_RIGHT;
break;
case (int)MoveDir.UL:
move_speed_dir = MOVE_UP - MOVE_RIGHT;
break;
case (int)MoveDir.UR:
move_speed_dir = MOVE_UP + MOVE_RIGHT;
break;
case (int)MoveDir.DL:
move_speed_dir = -MOVE_UP - MOVE_RIGHT;
break;
case (int)MoveDir.DR:
move_speed_dir = -MOVE_UP + MOVE_RIGHT;
break;
}
return move_speed_dir.normalized;
} //开火
public const KeyCode INPUT_FIRE = KeyCode.Space;
//开火时间cd
public float fireCD = 0.5f;
//上次开火时间
float lastTime = ;
//当前时间
float curTime = ; void CheckFire()
{
if(isFire)
{
Fire();
}
} void Fire()
{
curTime = Time.time;
if(curTime - lastTime > fireCD)
{
//创建子弹
CreateBullet();
lastTime = curTime;
}
} //画布
public Canvas canvas;
//创建子弹
void CreateBullet()
{
GameObject bullet = Instantiate(prefabBullet, target.localPosition, target.rotation);
bullet.transform.SetParent(canvas.transform, false);
}
}

还是之前的移动代码,增加了

这里是需要先把Bullet制作成Prefab预制体,使用Resources.Load方法载入子弹prefab

在Project下创建文件夹Resources(必须用这个才可以载入)下面Prefab里面都是做好的预制体,做法就是把Hierarchy下的对象直接拖到Prefab文件夹下(请无视与本例无关的东西)

ChangeKeyPressState()方法里面增加判断开火的按键,FixedUpdate里面检测是不是开火,if里面可以加很多条件,比如CG时候不能开火,没有子弹不能开火等等。

Fire()这个方法里面记录了开火的当前时间,下次开火的时候判断是不是已经过了CD时长,不然按住按键,每帧都会开火,火力太猛没法玩了,氪金大佬专属。

CreateBullet()这里面使用了Instantiate这个自带的方法,通过前面载入的子弹预设体prefab可以克隆出bullet(就是通过模版复制的过程)

切记要把bullet的父物体设置成Canvas,就是通过代码把bullet放到canvas下面,不然你子弹可能不在UI里面哦~

SetParent()方法的第二个参数用false,这样可以保证bullet的size不会因为父节点变化而变化。好奇的话,你可以写成true试试

来试试发射子弹,打到敌人就消失了。

不过这敌人木头人太愣了,给他点反应吧,enemy1上添加脚本enemy1(脚本名字你随便起)

 using System.Collections;
using System.Collections.Generic;
using UnityEngine; public class Enemy : MonoBehaviour {
void ByHit()
{
Debug.Log("byHit");
this.transform.Translate(Vector2.one);
}
}

就写一个方法,被揍了,效果就是朝着vector2.one也就是(1,1)这个方向移动。你想让有啥反映就在这里写就可以,想变大变小变色都没问题

那么怎么才能被调用呢?

 void OnTriggerEnter2D(Collider2D other)
{
Debug.Log("enter");
if (other.gameObject.tag == "Enemy")
{
other.gameObject.SendMessage("ByHit");
Destroy(this.gameObject);
}
}

刚才子弹的碰撞里面,增加sendMessage这个方法,参数是被调用方法的名字。这样就可以调用到碰撞的对方身上的脚本里面的"ByHit"这个方法

虽然敌人暂时还不能反击,不过起码不那么木讷了∠( ᐛ 」∠)_

敌人怎么反击的话,大家自己发挥想象力去做就好了,让他发出散弹打你也是可以的哦~

但愿你可以不用参考这个工程就能自己做出来https://pan.baidu.com/s/1e9rQIJt8AYDgHVPvbyJEuQ

不过,这个敌人为啥打不死呢?!哦,因为他没有血条啊!那咋整啊?下一篇介绍制作通用进度条(血条)(= ̄ω ̄=)