当前位置:网站首页>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 :
边栏推荐
- Practical application of digital twins: smart city project construction solution
- 若依框架代码生成详解
- Penetration test - post penetration - Trace cleaning
- The diagram of user login verification process is well written!
- 技术风向标 | 云原生技术架构成熟度模型解读
- Typescript TS basic knowledge interface, generics
- Reading notes of Kazuo Inamori's advice to young people
- easyui中textbox在光标位置插入内容
- Spark Learning Notes (IV) -- spark core programming RDD
- DNS record type and explanation of related terms
猜你喜欢

Fastboot刷机

DTS is equipped with a new self-developed kernel, which breaks through the key technology of the three center architecture of the two places Tencent cloud database

Introduction to database - Introduction to database

客户案例 | 关注老年用户体验,银行APP适老化改造要避虚就实

MySQL的数据库有关操作

网络安全/渗透测试工具AWVS14.9下载/使用教程/安装教程

How many implementation postures of delay queue? Daily essential skills!

Spark Learning Notes (VI) -- spark core core programming RDD action operator

复盘:DFS与BFS的主要区别,在思想上的区别,代码实现上的区别

Contour detection based on OpenCV (2)
随机推荐
网络安全/渗透测试工具AWVS14.9下载/使用教程/安装教程
2022牛客多校第二场的J -- 三分做法
数据库使用安全策略
Mysql database related operations
Banyan data model of Bairong
Graphic SQL, this is too vivid!
索引最佳实践
Technology vane | interpretation of cloud native technology architecture maturity model
The diagram of user login verification process is well written!
Basic concept and essence of Architecture
Spark Learning Notes (IV) -- spark core programming RDD
MySQL中文失败问题
[机缘参悟-52]:交浅言深要因人而异
Mysql: summary of common sub database and sub table schemes of Internet companies
快速排序及优化
Insert pictures and videos in typera
Spark: calculate the average value of the same key in different partitions (entry level - simple implementation)
Typescript TS basic knowledge interface, generics
Double disk: the main differences between DFS and BFS, the differences in ideology, and the differences in code implementation
DTS搭载全新自研内核,突破两地三中心架构的关键技术|腾讯云数据库