当前位置:网站首页>Cocos game practice-05-npc and character attack logic
Cocos game practice-05-npc and character attack logic
2022-07-27 03:42:00 【【03】】
add to NPC Attack logic and animation
assets/Scripts/WoodenSkeleton/WoodenSkeletonManager.ts
Determine whether the role is NPC In the upper, lower, left and right grids of ; At present X Coordinate equals , And Y The distance between coordinates is less than or equal to 1 grid , perhaps Y Coordinate equals ,X The coordinates are less than or equal to 1 grid
async init() {
// Attack detection , Role appears in NPC near ,NPC Active attack
EventManager.Instance.on(EVENT_ENUM.PLAYER_MOVE_END, this.onAttack, this)
}
// NPC Attack role detection
onAttack() {
const {
x: playerX, y: playerY } = DataManager.Instance.player
// Determine whether the role is NPC In the upper, lower, left and right grids of
const disX = Math.abs(this.x - playerX)
const disY = Math.abs(this.y - playerY)
// At present X Coordinate equals , And Y The distance between coordinates is less than or equal to 1 grid , perhaps Y Coordinate equals ,X The coordinates are less than or equal to 1 grid
if ((this.x === playerX && disY <= 1) || (this.y === playerY && disX <= 1)) {
this.state = ENTITY_STATE_ENUM.ATTACK
} else {
this.state = ENTITY_STATE_ENUM.IDLE
}
}
assets/Scripts/WoodenSkeleton/AttackSubStateMachine.ts
add to NPC Attack animation
const BASE_URL = 'texture/woodenskeleton/attack'
@ccclass('AttackSubStateMachine')
export class AttackSubStateMachine extends DirectionSubStateMachine {
constructor(fsm: StateMachine) {
super(fsm)
// NPC Attack animation
this.stateMachines.set(DIRECTION_ENUM.TOP, new State(fsm, `${
BASE_URL}/top`))
this.stateMachines.set(DIRECTION_ENUM.BOTTOM, new State(fsm, `${
BASE_URL}/bottom`))
this.stateMachines.set(DIRECTION_ENUM.LEFT, new State(fsm, `${
BASE_URL}/left`))
this.stateMachines.set(DIRECTION_ENUM.RIGHT, new State(fsm, `${
BASE_URL}/right`))
}
}
assets/Scripts/WoodenSkeleton/WoodenSkeletonStateMachine.ts
Register the state machine , Add the attack state to the state machine
// Initialize parameters
initParams() {
//...
this.params.set(PARAMS_NAME_ENUM.ATTACK, initParamsTrigger())
}
// Initialize state machine
initStateMachine() {
//...
// NPC Fight animation
this.stateMachines.set(PARAMS_NAME_ENUM.ATTACK, new AttackSubStateMachine(this))
}
// Initialize animation
initAnimationEvent() {
this.animationComponent.on(Animation.EventType.FINISHED, () => {
// After executing the animation, you need to restore the default idle Animation white list
const whiteList = ['attack']
const name = this.animationComponent.defaultClip.name
if (whiteList.some(v => name.includes(v))) {
// Unified state entrance
this.node.getComponent(EntityManager).state = ENTITY_STATE_ENUM.IDLE
// this.currentState = this.stateMachines.get(PARAMS_NAME_ENUM.IDLE)
}
})
}
run() {
//...
case this.stateMachines.get(PARAMS_NAME_ENUM.ATTACK):
if (this.params.get(PARAMS_NAME_ENUM.ATTACK).value) {
this.currentState = this.stateMachines.get(PARAMS_NAME_ENUM.ATTACK)
} else if...
}
Add character death logic and animation
assets/Scripts/WoodenSkeleton/WoodenSkeletonManager.ts
stay NPC The attack Department notifies the character of death
// ...
// NPC Attack role detection
onAttack() {
const {
x: playerX, y: playerY, state: playState } = DataManager.Instance.player
// Determine whether the role is NPC In the upper, lower, left and right grids of
const disX = Math.abs(this.x - playerX)
const disY = Math.abs(this.y - playerY)
// At present X Coordinate equals , And Y The distance between coordinates is less than or equal to 1 grid , perhaps Y Coordinate equals ,X The coordinates are less than or equal to 1 grid , And judge that the current character is not dead , Otherwise, there will be whiplash phenomenon
if (
((this.x === playerX && disY <= 1) || (this.y === playerY && disX <= 1)) &&
playState !== ENTITY_STATE_ENUM.DEATH &&
playState !== ENTITY_STATE_ENUM.AIRDEATH
) {
this.state = ENTITY_STATE_ENUM.ATTACK
// Notification role , Die on the ground
EventManager.Instance.emit(EVENT_ENUM.ATTACK_PLAYER, ENTITY_STATE_ENUM.DEATH)
} else {
this.state = ENTITY_STATE_ENUM.IDLE
}
}
// ...
assets/Scripts/Player/PlayerManager.ts
Register character death events
// ...
async init() {
//...
EventManager.Instance.on(EVENT_ENUM.ATTACK_PLAYER, this.onDead, this)
}
// The character dies , Just give the status directly
onDead(type: ENTITY_STATE_ENUM) {
this.state = type
}
inputHandler(inputDirection: CONTROLLER_ENUM) {
// Cannot move after death
if (this.state === ENTITY_STATE_ENUM.DEATH || this.state === ENTITY_STATE_ENUM.AIRDEATH) {
return
}
// ...
}
assets/Scripts/Player/DeathSubStateMachine.ts
Add character death animation
const BASE_URL = 'texture/player/death'
@ccclass('DeathSubStateMachine')
export class DeathSubStateMachine extends DirectionSubStateMachine {
constructor(fsm: StateMachine) {
super(fsm)
// Character death animation
this.stateMachines.set(DIRECTION_ENUM.TOP, new State(fsm, `${
BASE_URL}/top`))
this.stateMachines.set(DIRECTION_ENUM.BOTTOM, new State(fsm, `${
BASE_URL}/bottom`))
this.stateMachines.set(DIRECTION_ENUM.LEFT, new State(fsm, `${
BASE_URL}/left`))
this.stateMachines.set(DIRECTION_ENUM.RIGHT, new State(fsm, `${
BASE_URL}/right`))
}
}
assets/Scripts/Player/PlayerStateMachine.ts
Register character death animation
// ...
// Initialize parameters
initParams() {
//...
this.params.set(PARAMS_NAME_ENUM.DEATH, initParamsTrigger())
}
// Initialize state machine
initStateMachine() {
//...
// Death
this.stateMachines.set(PARAMS_NAME_ENUM.DEATH, new DeathSubStateMachine(this))
}
run() {
case this.stateMachines.get(PARAMS_NAME_ENUM.DEATH):
if (this.params.get(PARAMS_NAME_ENUM.DEATH).value) {
this.currentState = this.stateMachines.get(PARAMS_NAME_ENUM.DEATH)
} else if//...
}

Add character attack and animation
assets/Scripts/Player/PlayerManager.ts
Attack logic , The current direction is the same as the next step , And the front one is NPC, Then launch an attack
//...
inputHandler{
//...
// Whether you can attack at present NPC
if (this.willAttack(inputDirection)) {
return
}
//...
}
// Judge to attack the enemy , Similar to collision detection
willAttack(type: CONTROLLER_ENUM): boolean {
// all NPC
const enemies = DataManager.Instance.enemies
for (let i = 0; i < enemies.length; i++) {
const {
x: enemyX, y: enemyY } = enemies[i]
// If the current direction is up , And the next step is to go up ,NPC Of X Coordinates and roles X Coordinate equals , On the same line , And NPC Of Y Coordinates in the role Y In coordinates 2 grid , That is, in the front space of the weapon , Then trigger the attack
if (
type === CONTROLLER_ENUM.TOP &&
this.direction === DIRECTION_ENUM.TOP &&
enemyX === this.x &&
enemyY === this.targetY - 2
) {
this.state = ENTITY_STATE_ENUM.ATTACK
return true
} else if (
type === CONTROLLER_ENUM.BOTTOM &&
this.direction === DIRECTION_ENUM.BOTTOM &&
enemyX === this.x &&
enemyY === this.targetY + 2
) {
this.state = ENTITY_STATE_ENUM.ATTACK
return true
} else if (
type === CONTROLLER_ENUM.LEFT &&
this.direction === DIRECTION_ENUM.LEFT &&
enemyY === this.targetY &&
enemyX === this.targetX - 2
) {
this.state = ENTITY_STATE_ENUM.ATTACK
return true
} else if (
type === CONTROLLER_ENUM.RIGHT &&
this.direction === DIRECTION_ENUM.RIGHT &&
enemyY === this.targetY &&
enemyX === this.targetX + 2
) {
this.state = ENTITY_STATE_ENUM.ATTACK
return true
}
}
return false
}
assets/Scripts/Player/AttackSubStateMachine.ts
Add character attack animation
const BASE_URL = 'texture/player/attack'
@ccclass('AttackSubStateMachinePlayer')
export class AttackSubStateMachinePlayer extends DirectionSubStateMachine {
constructor(fsm: StateMachine) {
super(fsm)
// Character attack animation
this.stateMachines.set(DIRECTION_ENUM.TOP, new State(fsm, `${
BASE_URL}/top`))
this.stateMachines.set(DIRECTION_ENUM.BOTTOM, new State(fsm, `${
BASE_URL}/bottom`))
this.stateMachines.set(DIRECTION_ENUM.LEFT, new State(fsm, `${
BASE_URL}/left`))
this.stateMachines.set(DIRECTION_ENUM.RIGHT, new State(fsm, `${
BASE_URL}/right`))
}
}
assets/Scripts/Player/PlayerStateMachine.ts
Register attack animation
// ...
// Initialize parameters
initParams() {
//...
this.params.set(PARAMS_NAME_ENUM.ATTACK, initParamsTrigger())
}
// Initialize state machine
initStateMachine() {
//...
// Death
this.stateMachines.set(PARAMS_NAME_ENUM.ATTACK, new AttackSubStateMachinePlayer(this))
}
run() {
case this.stateMachines.get(PARAMS_NAME_ENUM.ATTACK):
if (this.params.get(PARAMS_NAME_ENUM.ATTACK).value) {
this.currentState = this.stateMachines.get(PARAMS_NAME_ENUM.ATTACK)
} else if//...
}
add to NPC Death logic and animation
assets/Base/EntityManager.ts
Add a random to the entity class id, figure ,NPC Will have one id
///...
export class EntityManager extends Component {
id: string = randomString(12)
//...
}
// Generate random string
export const randomString = (length: number): string => {
const str = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
let result = ''
for (let i = length; i > 0; --i) result += str[Math.floor(Math.random() * str.length)]
return result
}
assets/Scripts/Player/PlayerManager.ts
After adding an attack NPC The logic of death
//...
inputHandler(inputDirection: CONTROLLER_ENUM) {
//...
// Whether you can attack at present NPC
const attackId = this.willAttack(inputDirection)
if (attackId) {
// notice NPC death
EventManager.Instance.emit(EVENT_ENUM.ATTACK_ENEMY, attackId)
return
}
// The current character dies || The characters are not rendered || The current state is attack
if (
this.state === ENTITY_STATE_ENUM.DEATH ||
this.state === ENTITY_STATE_ENUM.AIRDEATH ||
this.state === ENTITY_STATE_ENUM.ATTACK
) {
return
}
//...
}
willAttack(type: CONTROLLER_ENUM): string {
//...
for (let i = 0; i < enemies.length; i++) {
// If NPC Dead , Skip the attack
if (enemies[i].state === ENTITY_STATE_ENUM.DEATH) {
continue
}
//...
const {
x: enemyX, y: enemyY, id: enemyId } = enemies[i]
//...
// In every attack situation , All back to NPC Of id
return enemyId
//...
return ''
}
assets/Scripts/WoodenSkeleton/WoodenSkeletonManager.ts
register NPC Death animation
//...
async init() {
//...
// Death
EventManager.Instance.on(EVENT_ENUM.ATTACK_ENEMY, this.onDead, this)
}
// NPC Towards character detection
onChangeDirection(isInit?: boolean) {
if (this.state === ENTITY_STATE_ENUM.DEATH || !DataManager.Instance.player) {
// Dead
return
}
//...
}
// NPC Attack role detection
onAttack() {
if (this.state === ENTITY_STATE_ENUM.DEATH || !DataManager.Instance.player) {
// Dead
return
}
//...
}
// NPC Death
onDead(attackId: string) {
if (this.state === ENTITY_STATE_ENUM.DEATH) {
return
}
// Judge the present NPC Death is death
if (this.id === attackId) {
this.state = ENTITY_STATE_ENUM.DEATH
}
}
assets/Scripts/WoodenSkeleton/DeathSubStateMachineWooden.ts
add to NPC Death animation
const BASE_URL = 'texture/woodenskeleton/death'
@ccclass('DeathSubStateMachineWooden')
export class DeathSubStateMachineWooden extends DirectionSubStateMachine {
constructor(fsm: StateMachine) {
super(fsm)
// NPC Death animation
this.stateMachines.set(DIRECTION_ENUM.TOP, new State(fsm, `${
BASE_URL}/top`))
this.stateMachines.set(DIRECTION_ENUM.BOTTOM, new State(fsm, `${
BASE_URL}/bottom`))
this.stateMachines.set(DIRECTION_ENUM.LEFT, new State(fsm, `${
BASE_URL}/left`))
this.stateMachines.set(DIRECTION_ENUM.RIGHT, new State(fsm, `${
BASE_URL}/right`))
}
}

Render the door and the door opening logic
assets/Scripts/Door/DeathSubStateMachineDoor.ts
Door open animation - The code is almost the same as other animation codes , Change one BASE_URL that will do , The code is slightly
assets/Scripts/Door/IdleSubStateMachineDoor.ts
Door closing animation - The code is slightly
assets/Scripts/Door/DoorManager.ts
Add door opening event and logic , When all NPC When they all die , Door open
@ccclass('DoorManager')
export class DoorManager extends EntityManager {
async init() {
this.fsm = this.addComponent(DoorStateMachine)
await this.fsm.init()
super.init({
x: 7,
y: 8,
type: ENTITY_TYPE_ENUM.PLAYER,
direction: DIRECTION_ENUM.TOP, // Set the initial direction
state: ENTITY_STATE_ENUM.IDLE, // Set up fsm Animate
})
EventManager.Instance.on(EVENT_ENUM.DOOR_OPEN, this.onOpen, this)
}
// Unbind event
onDestroy() {
super.onDestroy()
EventManager.Instance.off(EVENT_ENUM.DOOR_OPEN, this.onOpen)
}
onOpen() {
// The door is still there , all NPC Are dead , Just let the door disappear
if (
this.state !== ENTITY_STATE_ENUM.DEATH &&
DataManager.Instance.enemies.every(enemy => enemy.state === ENTITY_STATE_ENUM.DEATH)
)
this.state = ENTITY_STATE_ENUM.DEATH
}
}
assets/Scripts/Door/DoorStateMachine.ts
Register the door open close animation , Like other registration methods
@ccclass('DoorStateMachine')
export class DoorStateMachine extends StateMachine {
resetTrigger() {
for (const [_, value] of this.params) {
if (value.type === FSM_PARAMS_TYPE_ENUM.TRIGGER) {
value.value = false
}
}
}
// Initialize parameters
initParams() {
this.params.set(PARAMS_NAME_ENUM.IDLE, initParamsTrigger())
this.params.set(PARAMS_NAME_ENUM.DIRECTION, initParamsNumber())
this.params.set(PARAMS_NAME_ENUM.DEATH, initParamsTrigger())
}
// Initialize state machine
initStateMachine() {
// Door initialization
this.stateMachines.set(PARAMS_NAME_ENUM.IDLE, new IdleSubStateMachineDoor(this))
// Door disappears
this.stateMachines.set(PARAMS_NAME_ENUM.DEATH, new DeathSubStateMachineDoor(this))
}
// Initialize animation
initAnimationEvent() {
}
async init() {
// Add animation components
this.animationComponent = this.addComponent(Animation)
this.initParams()
this.initStateMachine()
this.initAnimationEvent()
// Make sure the resource is loaded
await Promise.all(this.waitingList)
}
run() {
switch (this.currentState) {
case this.stateMachines.get(PARAMS_NAME_ENUM.IDLE):
case this.stateMachines.get(PARAMS_NAME_ENUM.DEATH):
if (this.params.get(PARAMS_NAME_ENUM.DEATH).value) {
this.currentState = this.stateMachines.get(PARAMS_NAME_ENUM.DEATH)
} else if (this.params.get(PARAMS_NAME_ENUM.IDLE).value) {
this.currentState = this.stateMachines.get(PARAMS_NAME_ENUM.IDLE)
} else {
// In order to trigger the change of sub state machine
this.currentState = this.currentState
}
break
default:
this.currentState = this.stateMachines.get(PARAMS_NAME_ENUM.IDLE)
}
}
}
assets/Scripts/Player/PlayerManager.ts
Triggering event , whenever NPC Trigger detection at death
//...
inputHandler(inputDirection: CONTROLLER_ENUM) {
if (this.isMoving) {
return
}
// Whether you can attack at present NPC
const attackId = this.willAttack(inputDirection)
if (attackId) {
// notice NPC death
EventManager.Instance.emit(EVENT_ENUM.ATTACK_ENEMY, attackId)
// Inform to judge whether the door is hidden
EventManager.Instance.emit(EVENT_ENUM.DOOR_OPEN)
return
}
//...
}

Source code address of this section :
边栏推荐
- Reading notes of Kazuo Inamori's advice to young people
- [tree chain dissection] 2022 Hangzhou Electric Multi school 21001 static query on tree
- MySQL has a nonexistent error
- docker 创建mysql 8.x容器,支持mac ,arm架构芯片
- 渗透测试-后渗透-痕迹清理
- Method of converting curtain article OPML into markdown
- Learn the recycling mechanism of recyclerview again
- Explain
- [从零开始学习FPGA编程-54]:高阶篇 - 基于IP核的FPGA开发-PLL锁相环IP核的原理与配置(Altera)
- Details of impala implementation plan
猜你喜欢
随机推荐
渗透测试-后渗透-痕迹清理
数字孪生实际应用:智慧城市项目建设解决方案
How to interact with the server when the client sends an SQL message
Deeply understand the underlying data structure and algorithm of MySQL index
unity游戏,隐私协议最简单解决方案!仅3行代码就搞定!(转载)
Fastboot刷机
flask_restful中reqparse解析器继承
PIP3 setting alicloud
Maximum continuous subsequence (day 77)
Vector to SVG method
DNS record type and explanation of related terms
Record the problem of PHP program accessing system files incorrectly
深入理解Mysql索引底层数据结构与算法
Number of square arrays (day 81)
30 minutes to thoroughly understand the synchronized lock upgrade process
The function and application of lpci-252 universal PCI interface can card
网络安全/渗透测试工具AWVS14.9下载/使用教程/安装教程
Introduction to redis
Double disk: the main differences between DFS and BFS, the differences in ideology, and the differences in code implementation
Docker creates MySQL 8.x container and supports Mac and arm architecture chips









