I am participating in the nuggets Community game creative submission Contest. For details, please see: Game Creative Submission Contest

preface

This will use vue3 and pixijs as the key technology to put the xianjian one to xianjian players relishes miles slope sword god terrier to write a small game, the terrier is a player in xianjian roughly one because I don’t know can be shipped out yuhang town, so can’t find the way out, idle bored at miles slope leveling, Unconsciously in the novice village to learn the last skill – sword god, this matter finally spread, the whole RPG world, “Ten li Po sword god” has become a legend on how boring people really. Of course, I also grew up playing Xianjian, before there was no network, xianjian one is to play 9 times, so, this time to create a xianjian fan games to remember the passing of childhood.

Without further ado, let’s first show kangkang how the effect is:

Demo address: jsmask. Gitee. IO/shilipojian…

introduce

The code structure of this issue is almost the same as that of the previous issue, which used vue3+ PixiJS to reproduce the game in my childhood memory – duck hunting season. Therefore, it will not talk about the repetitive content such as resource loading, switching, release and subscription, but only the scene dialogue, frame animation, collision detection, dropping props and other content.

Game shows

  1. Enter the game click on the monster can reduce its health, when the health is lower than 0 will be eliminated.
  2. When the monster is eliminated, the probability drops the prop, the prop is very many kinds, some increases the fighting time, some increases the attack power.
  3. Kill as many bogeymen as you can in your limited time to score as many points as possible. At the end of the game, the swordsmen are judged according to their score.

The main technical

  1. Vite: Responsible for the module building and packaging of the whole project.
  2. Vue3: as a front-end framework, it is convenient to complete the responsiveness and componentization of some interfaces.
  3. SCSS: Responsible for the initial loading of the CSS animations of the interface, working with some interface scaling styles.
  4. Mitt. Js: Responsible for publishing and subscribing.
  5. Pixi.js: The game engine where most of the tasks in the game are done.
  6. Gsap.js: Is responsible for some property animation operations.
  7. Bump.js: Does the collision detection task.

start

The scene dialogue

// talkData.js
export const talkData = [{
    name: "Li Xiaoyao".face: "hero_face_1".content: 'village and ten miles of slope to wander around, how haven't found the way to leave ah ~'
}, {
    name: "Wine Sword Fairy".face: "npc_face_0".content: 'Boy, didn't find a way out not as good as practicing martial arts in ten miles of slope, later go out also won't suffer a loss. `.position: "left"
}
  //....
]
Copy the code

Here we first write the dialog information to do, such as name, avatar, content, location and so on.

export default class Talk {
    // ...
    show({ name, face, content, position }) {
        this.target.visible = false;
        return new Promise((resolve, reject) = > {
            this.clearChildren();
            this.drawInfo({ name, face, content, position });
            this.target.visible = true;
            Bus.$on("talk_next".() = > {
                Bus.$off("talk_next")
                resolve()
            })
        })
    }
    // ...
}
Copy the code

Also write a conversation class that we expect to instantiate and call each time with the show method, which is asynchronous and emits a talk_next event when the screen is clicked before proceeding to the next conversation.

Here we can think about how it keeps the conversation going, and of course you can use the following method:

async stageTalk() {
    await this.talk.show.call(this.talk, talkData[0])
    await this.talk.show.call(this.talk, talkData[1])
    await this.talk.show.call(this.talk, talkData[2])
    // ...
}
Copy the code

But you’ll find that it’s rather inelegant and adds a lot of extra manual labor.

Improvements can be made in the following ways:

// mainScene.js
import Scene from "./scene"
import { talkData, talkResult } from "./talkData"

export default class MainScene extends Scene {
    // ...
    async stageTalk() {
        let talkList = []
        for (let i = 0; i < talkData.length; i++) {
            talkList.push(this.talk.show.bind(this.talk, talkData[i]))
        }
        return new Promise(async (resove, reject) => {
            while (talkList.length > 0) {
                await talkList.shift()()
            }
            resove()
        })
	},
    // ...
}
Copy the code

This is doing our initial dialogue scenario, and you can see that we’re passing in the show method of the conversation instance and storing it in the talkList array, and then doing the asynchronous operation, going through the while loop until the talkList array is finished.

Replace code like this:

The frame of animation

import { AnimatedSprite } from "pixi.js";
class Hero {
    // ...
    attack() {
        if (this.state === HERO_STATE.attack) return;
        this.state = HERO_STATE.attack;
        playAttack();
        this.clearChildren();
        let attack_list = []
        for (let i = 0; i < 3; i++) {
            attack_list.push(createSprite({ name: "hero_attack_" + i, anchor: 0 }).texture);
        }
        this.attackAnimatedSprite = new AnimatedSprite(attack_list);
        this.attackAnimatedSprite.loop = false;
        this.attackAnimatedSprite.animationSpeed = 32.;
        this.attackAnimatedSprite.gotoAndPlay(0);
        this.attackAnimatedSprite.onComplete = () = > {
            this.normal()
        }
        this.target.addChild(this.attackAnimatedSprite)
    }
    // ...
}
Copy the code

We use the AnimatedSprite that comes with Pixi.js for all our frame animations. It’s very simple to use. We first store the Sprite textures that will be animated in sequence into an array, and then instantiate the AnimatedSprite to pass in the array. This can be carried out by gotoAndPlay and other methods, of course, you can control whether it is loop, play speed, after the completion of the event, don’t forget to store in the target node to display, processing some simple frame animation animation in this way to complete is very convenient.

Collision detection

// tools.js
import * as PIXI from "pixi.js"
import Bump from "bump.js"
export const bump = new Bump(PIXI)
Copy the code

The bump.js library helps us determine whether the mouse has hit a bogeyman in the game, and to use it in PIXI, we must generate an instance of it by passing in the PIXI.

// enemy.js
import { bump } from ".. /tools"
class Enemy {
    // ...
    hunt(e) {
        if (this.hp <= 0 || this.state ! == ENEMY_STATE.normal)return;
        if(! bump.hit(e.pos,this.target, true.true)) return;
        this.hp -= e.power;
        this.health.cut(this.hp)
        playHit();
        this.hp <= 0 && this.die();
    }
    // ...
}
Copy the code

Bump. Hit method is used to detect the collision between a point and the current enemy. If it is true, the point is proved to be hit, and the corresponding logic such as blood drop is carried out.

Drop props

const createEnemyData = () = > {
    return {
        yong: {
            hp: 100.speed: 78..animationSpeed: 1..score: 7.itemType: 1.chance: 30
        },
        // ...}}Copy the code

Here is the basic data to generate enemy-pupa, where chance represents the probability of the item dropping and itemType represents the type of item dropping.

// mainScene.js
import Scene from "./scene"
import Item from "./item"

export default class MainScene extends Scene {
    // ...
    addCount(obj) {
        // ...
        // Random item drop instances
        if (random(0.100) < obj.chance) {
            new Item({
                x: obj.diePos.x,
                y: obj.diePos.y,
                stage: this.stageContainer,
                hero: this.hero,
                type: obj.itemType
            })
        }
        
        // Increase the number of runs and kills
        this.score += obj.score;
        this.count += 1;
	}
    // ...
}
Copy the code

Here is when the devil was struck, emit addCount events, we execute, monsters will pass the current drop items the probability of a random number, if we come to drop conditions, will generate a prop instance and, of course, this item appears after the next to arrive will automatically perform a role animation, when addBuff will be issued after the completion of the event, To add corresponding abilities to the character.

conclusion

Pixi.js deals with some scene layouts, and collision detection is not very intuitive. If you want to make bigger H5 games, COcos Creator is still recommended, although it is heavy. Since this deployment is online using the free Gitee Pages, it may not be too fast to open, so keep the code and resources as light as possible.

This material is relatively limited, but also as far as possible in accordance with the style of the new version of xianjian a reduction out, including the text are used in traditional, pay tribute to the Taiwan Daewoo. When I was a child, I only played games for two hours a week. Although it was very short, I miss that time very much. In the future, I will realize more classic games in my childhood, and I hope to get your encouragement and support