麦扣 3DRPG游戏开发教程 15-16集

麦扣 3DRPG游戏开发教程 15-16集

🎮

15 制作一系列关于数据的

ScriptableObject

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "New Data", menuName = "Character Stats/Data")]
//这个能帮我们在Create菜单当中创建一个子集的菜单
public class CharacterData_SO : ScriptableObject
{
//能想到的所有状态都可以罗列出来
[Header("Stats Info")] public int maxHealth;
public int currentHealth;
public int baseDefence;
public int currenrDefence;
}

保存不需要任何操作 就自动会生成

xdESSI.png

这便是scriptObject的好处

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CharacterStats : MonoBehaviour
{
public CharacterData_SO characterData;

//get 可读 set可写入

#region Read from Data_SO

public int MaxHealth
{
get
{
if (characterData != null)
return characterData.maxHealth;
else return 0;
}
set { characterData.maxHealth = value; }
}

public int CurrentHealth
{
get
{
if (characterData != null) return characterData.currentHealth;
else return 0;
}
set { characterData.currentHealth = value; }
}

public int BaseDefence
{
get
{
if (characterData != null)
{
return characterData.baseDefence;
}
else return 0;
}
set { characterData.baseDefence = value; }
}

public int CurrentDefence
{
get
{
if (characterData != null)
{
return characterData.currenDefence;
}
else return 0;
}
set { characterData.currenDefence = value; }
}

#endregion
}

16攻击相关

重复15集的步骤 添加攻击属性,然后在CharacterStats.CS内调用

然后返回unity 去给它赋值


攻击动画系统里一般都是判断trigger

然后在代码里set一下


暴击一般设置两个动画状态

attack (trigger) | 非暴击👉 | Critical (true)

Critical (true) | 👈暴击 | Critical (false)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using Random = System.Random;


public enum EnemyStates
{
GUARD,
PATROL,
CHASE,
DEAD
}

[RequireComponent(typeof(NavMeshAgent))]
public class EnemyController : MonoBehaviour
{
private NavMeshAgent agent;
private EnemyStates enemyStates;
private Animator anim;
private CharacterStats characterStats;
[Header("Basic Settings")] public float sightRadius;

private float speed;
public bool isGuard;

private GameObject attackTarget;

//怪的巡逻
public float lookAtTime;
private float remainLookAtTime;
private float lastAttackTime;
[Header("Patrol State")] public float patrolRange;
private Vector3 wayPoint;
private Vector3 guardPos;

//bool配合动画
private bool isWalk;

private bool isChase;
private bool isFollow;

private void Awake()
{
//自有组件、自有变量放在awake
anim = GetComponent<Animator>();
agent = GetComponent<NavMeshAgent>();
speed = agent.speed;
guardPos = transform.position;
remainLookAtTime = lookAtTime;
characterStats = GetComponent<CharacterStats>();
}

private void Start()
{
if (isGuard)
{
enemyStates = EnemyStates.GUARD;
}
else
{
enemyStates = EnemyStates.PATROL;
GetNewWayPoint();
}
}

private void Update()
{
SwitchStates();
SwitchAnimation();
lastAttackTime -= Time.deltaTime;
}

void SwitchAnimation()
{
anim.SetBool("Walk", isWalk);
anim.SetBool("Chase", isChase);
anim.SetBool("Follow", isFollow);
anim.SetBool("Critical",characterStats.isCritical);
}

//状态切换机器
void SwitchStates()
{
//如果发现player 切换到chase
if (FoundPlayer())
{
enemyStates = EnemyStates.CHASE;
}


//--------------------------
switch (enemyStates)
{
case EnemyStates.GUARD:
break;
case EnemyStates.PATROL:
isChase = false;
agent.speed = speed * .5f;
//判断一下指定的点 如果相同就给一个新的点,不同就往这儿移动
if (Vector3.Distance(wayPoint, transform.position) <= agent.stoppingDistance)
{
isWalk = false;
if (remainLookAtTime > 0)
remainLookAtTime -= Time.deltaTime;
else
GetNewWayPoint();
}
else
{
isWalk = true;
agent.destination = wayPoint;
}

break;
case EnemyStates.CHASE:
//追逐玩家
//在攻击范围内攻击
//配合动画
isWalk = false;
isChase = true;

agent.speed = speed;
if (!FoundPlayer())
{
//拉脱回上一个状态
isFollow = false;
if (remainLookAtTime > 0)
{
agent.destination = transform.position;
remainLookAtTime -= Time.deltaTime;
}
else if (isGuard)
{
enemyStates = EnemyStates.GUARD;
}
else
{
enemyStates = EnemyStates.PATROL;
}
}
else
{
isFollow = true;
agent.isStopped = false;
agent.destination = attackTarget.transform.position;
}

if (TargetInAttackRange() || TargetInSkillRange())
{
isFollow = false;
agent.isStopped = true;
if (lastAttackTime < 0)
{
lastAttackTime = characterStats.attackData.coolDown;
//暴击判断
characterStats.isCritical = UnityEngine.Random.value < characterStats.attackData.criticalChance;
//执行攻击
Attack();
}
}

break;
case EnemyStates.DEAD:
break;
}
}

void Attack()
{
transform.LookAt(attackTarget.transform);
if (TargetInAttackRange())
{
//近身攻击动画
anim.SetTrigger("Attack");
}
if (TargetInSkillRange())
{
//技能攻击动画
anim.SetTrigger("Skill");
}
}

bool FoundPlayer()
{
var colliders = Physics.OverlapSphere(transform.position, sightRadius);
foreach (var target in colliders)
{
if (target.CompareTag("Player"))
{
attackTarget = target.gameObject;
return true;
}
}

attackTarget = null;
return false;
}

bool TargetInAttackRange()
{
if (attackTarget != null)
return Vector3.Distance(attackTarget.transform.position, transform.position) <=
characterStats.attackData.attackRange;
else

return false;
}

bool TargetInSkillRange()
{
if (attackTarget != null)
return Vector3.Distance(attackTarget.transform.position, transform.position) <=
characterStats.attackData.skillRange;
else

return false;
}

// todo: 随机生成一个点
void GetNewWayPoint()
{
remainLookAtTime = lookAtTime;
float randomX = UnityEngine.Random.Range(-patrolRange, patrolRange);
float randomZ = UnityEngine.Random.Range(-patrolRange, patrolRange);
// Vector3 randomPoint = new Vector3(transform.position.x + randomX, transform.position.y,
// transform.position.z + randomZ);
Vector3 randomPoint = new Vector3(guardPos.x + randomX, transform.position.y,
guardPos.z + randomZ);
//y还是保持它自身的刚体影响,因为地面上有坑坑洼洼
// todo: 可能出现的问题,不能这么简单就得到这个点
// wayPoint = randomPoint;
NavMeshHit hit;
wayPoint = NavMesh.SamplePosition(randomPoint, out hit, patrolRange, 1) ? hit.position : transform.position;
//这个函数返回一个bool值,√使得怪物的最终点为这个,否则停留于原地
}

private void OnDrawGizmosSelected()
{
Gizmos.color = Color.blue;
Gizmos.DrawWireSphere(transform.position, sightRadius);
}
}

麦扣 3DRPG游戏开发教程 15-16集

https://www.llbwy.com/2022/10/15/b站m_studio3drpg15-16/

作者

发布于

2022-10-15

更新于

2023-01-10

许可协议