Small knowledge, big challenge! This paper is participating in theEssentials for programmers”Creative activities
This article also participated in the “Digitalstar Project” to win a creative gift package and creative incentive money
introduce
In the last issue, we talked about how to compress and optimize the font resources and picture resources of the game. In this issue, we will specifically realize the case of inuyasha’s broken iron teeth. As shown in the figure:
After the last operation, we have the font and image resources, this will learn how to use these resources in pixi. Js to complete the dialogue scenes of production, we will roughly from installation, infrastructure, resource loading, resource mapping, typewriter, switch the picture and other aspects to achieve it, will soon begin to cough up ~
Implement article
1. Install dependencies
We will use Vite to build this project.
yarn add -D vite
Copy the code
Then we need to install pixi.js again
yarn add pixi.js
Copy the code
2. Infrastructure
We will create a new index.html file and write the basic style in it. Of course, we will import js with module mode because we will use pixi.js which we just downloaded
<body>
<div id="app"></div>
<script type="module" src="./app.js"></script>
</body>
Copy the code
* {
padding: 0;
margin: 0;
}
html.body {
width: 100%;
height: 100vh;
position: relative;
overflow: hidden;
background-color: # 000;
display: flex;
align-items: center;
justify-content: center;
}
#app {
width: 800px;
height: 600px;
background-color: white;
}
Copy the code
The main idea here is to size the #app of the main container and center it.
Next, we need to complete the basic structure of app.js, which is our main logic.
/*app.js*/
import * as PIXI from "pixi.js"
import data from "./data"
class Application {
constructor() {
this.app = null; / / PIXI. Application examples
this.stage = null; // Game stage
this.talkStage = null; // Dialogue scene container
this.w = 800; // Width of the scene
this.h = 600; // Height of the scene
this.stageIndex = 0; // The current subscript of the scene
this.stageTextures = {}; // Scene resources
this.charTimer = []; // Set of typewriters timer
this.init();
}
init() {
/ / initialization
let el = document.getElementById("app")
this.app = new PIXI.Application({
resizeTo: el,
backgroundColor: 0xFFFFFFF
});
this.w = this.app.view.width;
this.h = this.app.view.height;
this.stage = this.app.stage;
this.talkStage = new PIXI.Container();
this.stage.addChild(this.talkStage);
el.appendChild(this.app.view);
this.load();
this.bindEvent();
}
bindEvent() {
// Bind events
}
changeStageIndex() {
// Switch scenes
}
render() {
/ / rendering
this.drawBgImg();
this.drawTalk();
}
load() {
/ / load
}
drawBgImg() {
// Draw the background image
}
drawTalk() {
// Draw dialogue
this.drawContent();
}
drawContent() {
// Draw dialogue}}window.onload = new Application();
Copy the code
Here are the main things we do in master logic:
-
Pixi.js and the script data data.js used were introduced. Pixi.js will be my engine throughout. And data is the dialog script data that we wrote ourselves. As shown in the picture below, each scene in our dialogue and the characters and content of the dialogue are written here:
-
Second, we perform the initialization event to instantiate pixi.application, where in order to directly fill the #app container, we can use the resizeTo(EL) method to directly fill it, instead of setting it by width and height. Finally, don’t forget to fill the canvas into the #app container with el. AppendChild (app.view).
-
App. stage is the stage of the game where the content to be rendered can be placed, but we expect each scene to have a stage for easy control, so the talkStage instantiated by Pixi. Container is added to the main scene app.stage as the main Container for the dialogue scene. The content and pictures of all subsequent dialogue scenes will be appended to the talkStage.
3. Load resources
We have finished the preparation of resources before. In load, we can use Promise+ Loader to directly save the resource data we use after the initial loading, and then we can render.
init(){
// ...
this.load().then(res= > {
this.stageTextures = res.resources.stage.textures;
this.render();
})
// ...
}
load() {
return new Promise((resolve, reject) = > {
this.app.loader
.add('hk'.'font/hk/font.fnt')
.add("stage"."assets/stage.json") .load(resolve); })}Copy the code
4. Resource drawing
drawBgImg() {
const {stageIndex, stageTextures, talkStage} = this;
let _img = data[stageIndex].img;
if(! _img)return;
let img = stageTextures[_img + ".png"];
let bgImg = new PIXI.Sprite(img);
bgImg.anchor.set(0.0);
bgImg.position.set(0, -5)
bgImg.scale.set(1.23)
talkStage.addChild(bgImg);
}
drawTalk() {
const {stageIndex, stageTextures, stage} = this;
let _talk = data[stageIndex] ? data[stageIndex].talk : [];
if(! _talk || ! _talk.length)return;
this.drawContent(_talk);
}
drawContent(res) {
const contentContainer = new PIXI.Container();
contentContainer.position.set(30.this.h - 210);
contentContainer.width = this.w - 120
this.talkStage.addChild(contentContainer);
const box = new PIXI.Graphics();
box.beginFill(0x000000);
box.drawRect(0.0.this.w - 60.180);
box.endFill();
box.alpha = 0.61;
contentContainer.addChild(box);
const nameText = new PIXI.BitmapText(res[0].name + ":", {
fontName: 'Hua Kang Girl font'.fontSize: 32.tint: 0xFFFFFF.letterSpacing: 5.textHeight: 32.align: "left"}); nameText.position.set(30.15)
contentContainer.addChild(nameText);
const contentText = new PIXI.BitmapText(res[0].content, {
fontName: 'Hua Kang Girl font'.fontSize: 32.tint: 0xFFFFFF.letterSpacing: 3.textHeight: 120.textWidth: this.w - 120.align: "left".maxWidth: this.w - 120
});
contentText.position.set(30.55)
this.talkStage.addChild(contentText);
contentContainer.addChild(contentText);
}
Copy the code
Seemingly a lot, most of them are tedious repetitive business, and positioning, nesting. Just remember two things:
- Image with pixi. Sprite into the corresponding texture data to achieve rendering
- BitmapText is drawn with pixi.bitmaptext passing in the text and font to be drawn and some font properties
This completes the first scene interface
5. Typewriter effects
If we play A Lot of Japanese RPGS, we’ll find that a lot of dialogue is done individually with typewriter effects, which is very immersive. We’ll implement it in just a few lines.
drawContent(res) {
// ...
const contentText = new PIXI.BitmapText("", {
fontName: 'Hua Kang Girl font'.fontSize: 32.tint: 0xFFFFFF.letterSpacing: 3.textHeight: 120.textWidth: this.w - 120.align: "left".maxWidth: this.w - 120
});
Array.from(res[0].content).forEach((char, i) = >{(function(i) {
let timer = setTimeout(() = > {
contentText.text += char;
}, 200 * (i + 1))
this.charTimer.push(timer)
}.bind(this))(i)
})
// ...
}
Copy the code
We’re going to modify the drawContent method so that the content is just an empty string, and then we’re going to iterate over the content, adding one word every 200 milliseconds to create a typewriter effect. And we also need to save the timer for each word. If we switch to the next one before finishing the word, we will stop the timer and delete it.
render() {
if (this.charTimer.length) {
this.charTimer.forEach(timer= >{
clearTimeout(timer)
})
this.charTimer.length = 0;
};
this.talkStage.children.forEach((c, i) = > {
this.talkStage.removeChildAt(i)
})
this.drawBgImg();
this.drawTalk();
}
Copy the code
We will delete and empty the chartimers one by one, and we will also make sure that when we switch scenes we will empty the elements of the original scene.
6. Switch screens
Finally, the final step, we first bind the click event, then click the screen will be cut to the next image and text.
bindEvent() {
this.talkStage.interactive = true;
this.talkStage.buttonMode = true;
this.talkStage.on("pointerdown".this.changeStageIndex.bind(this));
}
changeStageIndex() {
if (this.stageIndex >= data.length - 1) return;
this.stageIndex += 1;
this.render();
}
Copy the code
We use the PointerDown click event to bind the method of switching scenes, and then add one to the stageIndex index if the current dialog data is not executed, and render the new screen. It’s as simple as that, and if it goes beyond that, we’re giving it a break here, which could actually jump into other logic, such as combat, mini-games, other dialogue, etc., depending on the specific business that the plan produces.
conclusion
This time we completely implemented dialogue scene animations for a simple game, and we could build on that to enrich the experience and functionality, but that’s the core of it. Although it is only a dialogue scene animation, we can realize that the loading and rendering of resources, the change of state, and the interaction of events can not escape a game. On this basis, we can also do animation, such as the typewriter just a few lines of code can enhance the experience of game substitution. Why should we give play to our thinking and create a story of our own