Skip to content

Commit 4740ed7

Browse files
author
Jean Poree
committed
Merge branch 'endscreen-module' into 'master'
[FEAT][SDK] adding the endscreen module See merge request codingame/game-engine!169
2 parents 443c71e + a62e71c commit 4740ed7

File tree

7 files changed

+426
-10
lines changed

7 files changed

+426
-10
lines changed

engine/modules/endscreen/pom.xml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<parent>
6+
<groupId>com.codingame</groupId>
7+
<artifactId>gameengine</artifactId>
8+
<version>master-SNAPSHOT</version>
9+
<relativePath>../../../pom.xml</relativePath>
10+
</parent>
11+
12+
<groupId>com.codingame.gameengine</groupId>
13+
<artifactId>module-endscreen</artifactId>
14+
<name>CodinGame Game Engine End Screen Module</name>
15+
<description>An end screen module for the CodinGame engine toolkit.</description>
16+
17+
<dependencies>
18+
<dependency>
19+
<groupId>com.codingame.gameengine</groupId>
20+
<artifactId>core</artifactId>
21+
<version>${project.version}</version>
22+
</dependency>
23+
</dependencies>
24+
</project>
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.codingame.gameengine.module.endscreen;
2+
3+
import com.codingame.gameengine.core.AbstractPlayer;
4+
import com.codingame.gameengine.core.GameManager;
5+
import com.codingame.gameengine.core.Module;
6+
import com.google.inject.Inject;
7+
import com.google.inject.Singleton;
8+
9+
/**
10+
* The EndScreen takes care of displaying and animating an end screen with the scores of the players at the end of the game.
11+
*
12+
*/
13+
@Singleton
14+
public class EndScreenModule implements Module {
15+
16+
private GameManager<AbstractPlayer> gameManager;
17+
private int[] scores;
18+
private String titleRankingsSprite = "logo.png";
19+
20+
@Inject
21+
EndScreenModule(GameManager<AbstractPlayer> gameManager) {
22+
this.gameManager = gameManager;
23+
gameManager.registerModule(this);
24+
}
25+
26+
/**
27+
* Send scores to the module
28+
*
29+
* @param scores
30+
* the scores of the different players, the index matches the player.getIndex()
31+
*/
32+
public void setScores(int[] scores) {
33+
this.scores = scores;
34+
}
35+
36+
/**
37+
* Allows you to set the sprite used as the title of the ranking board
38+
*
39+
* @param spriteName
40+
* the name of the sprite you want to use default is "logo.png"
41+
*/
42+
public void setTitleRankingsSprite(String spriteName) {
43+
this.titleRankingsSprite = spriteName;
44+
}
45+
46+
/**
47+
*
48+
* @return the name of the sprite that will be used as the title of the ranking board
49+
*/
50+
public String getTitleRankingsSprite() {
51+
return titleRankingsSprite;
52+
}
53+
54+
@Override
55+
public final void onGameInit() {
56+
}
57+
58+
@Override
59+
public final void onAfterGameTurn() {
60+
}
61+
62+
@Override
63+
public final void onAfterOnEnd() {
64+
Object[] data = { scores, titleRankingsSprite };
65+
gameManager.setViewData("endScreen", data);
66+
}
67+
68+
}
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
import {WIDTH, HEIGHT} from '../core/constants.js'
2+
import {lerp, unlerp} from '../core/utils.js'
3+
import {ErrorLog} from '../core/ErrorLog.js'
4+
import {MissingImageError} from './errors/MissingImageError.js'
5+
import {flagForDestructionOnReinit} from '../core/rendering.js'
6+
/* global PIXI */
7+
8+
export class EndScreenModule {
9+
constructor (assets) {
10+
this.states = []
11+
this.scores = []
12+
this.globalData = {}
13+
this.atEnd = false
14+
}
15+
16+
static get name () {
17+
return 'endScreen'
18+
}
19+
20+
updateScene (previousData, currentData, progress) {
21+
if (currentData.scores && progress === 1) {
22+
this.atEnd = true
23+
} else {
24+
this.atEnd = false
25+
}
26+
}
27+
28+
handleFrameData (frameInfo, data) {
29+
let scores = null
30+
let spriteName = null
31+
if (data) {
32+
scores = data[0]
33+
spriteName = data[1]
34+
}
35+
const state = {
36+
number: frameInfo.number,
37+
scores,
38+
spriteName
39+
}
40+
if (scores) {
41+
this.scores = scores
42+
}
43+
if (spriteName) {
44+
this.spriteName = spriteName
45+
}
46+
this.states.push(state)
47+
return state
48+
}
49+
50+
reinitScene (container, canvasData) {
51+
this.container = container
52+
this.endLayer = this.createEndScene(this)
53+
if (this.atEnd) {
54+
this.initEndScene()
55+
}
56+
this.container.addChild(this.endLayer)
57+
}
58+
59+
animateScene (delta) {
60+
let step = Math.min(32, delta)
61+
62+
if (this.atEnd) {
63+
if (!this.animationEnded) {
64+
this.renderEndScene(step)
65+
}
66+
} else {
67+
if (this.endTime > 0) {
68+
this.destroyEndScene()
69+
}
70+
this.endTime = 0
71+
}
72+
}
73+
74+
destroyEndScene () {
75+
this.animationEnded = false
76+
this.endLayer.visible = false
77+
}
78+
79+
initEndScene () {
80+
this.animationEnded = false
81+
this.endLayer.visible = true
82+
}
83+
84+
renderEndScene (step) {
85+
var endOfEnd = 10000
86+
if (this.endTime === 0) {
87+
this.initEndScene()
88+
}
89+
90+
var backS = 0
91+
var backD = 400
92+
var backP = unlerp(backS, backS + backD, this.endTime)
93+
this.endLayer.backgroundRanking.alpha = backP * 0.9
94+
95+
var logoS = 400
96+
var logoD = 600
97+
var logoP = unlerp(logoS, logoS + logoD, this.endTime)
98+
this.endLayer.titleRanking.scale.x = this.endLayer.titleRanking.scale.y = 0.001 + lerp(10, 0.8, logoP)
99+
this.endLayer.titleRanking.visible = !!logoP
100+
101+
var rankS = 1000
102+
var rankD = 300
103+
for (let i = 0; i < this.finishers.length; ++i) {
104+
var p = unlerp(rankS + rankD * i, rankS + rankD * i + rankD, this.endTime)
105+
this.finishers[i].alpha = p
106+
}
107+
108+
this.endTime += step
109+
110+
if (this.endTime >= endOfEnd) {
111+
this.animationEnded = true
112+
}
113+
}
114+
115+
handleGlobalData (players, globalData) {
116+
this.globalData = {
117+
players: players,
118+
playerCount: players.length
119+
}
120+
}
121+
122+
generateText (text, size, align, color) {
123+
var textEl
124+
125+
textEl = new PIXI.Text(text, {
126+
fontSize: Math.round(size / 1.2) + 'px',
127+
fontFamily: 'Lato',
128+
fontWeight: 'bold',
129+
fill: color
130+
})
131+
textEl.lineHeight = Math.round(size / 1.2)
132+
if (align === 'right') {
133+
textEl.anchor.x = 1
134+
} else if (align === 'center') {
135+
textEl.anchor.x = 0.5
136+
}
137+
return textEl
138+
}
139+
140+
createFinisher (finisher) {
141+
var layer = new PIXI.Container()
142+
143+
var avatarContainer = new PIXI.Container()
144+
avatarContainer.y = 0
145+
avatarContainer.x = 0
146+
147+
var backgroundAvatar = new PIXI.Graphics()
148+
backgroundAvatar.beginFill(0xffffff)
149+
backgroundAvatar.alpha = 0.1
150+
backgroundAvatar.drawRect(0, 0, 240, 120)
151+
avatarContainer.addChild(backgroundAvatar)
152+
153+
var avatarBorder = new PIXI.Graphics()
154+
avatarBorder.lineStyle(1, 0xffffff)
155+
avatarBorder.alpha = 0.5
156+
avatarBorder.drawRect(0, 0, 120, 120)
157+
avatarContainer.addChild(avatarBorder)
158+
159+
var avatar = new PIXI.Sprite(finisher.player.avatar)
160+
avatar.width = avatar.height = 120
161+
162+
var rank = this.generateText(finisher.rank.toString(), 76, 'center', finisher.player.color)
163+
rank.anchor.y = 0.5
164+
rank.position.x = 160
165+
rank.position.y = 56
166+
avatarContainer.addChild(rank)
167+
168+
var rankLetter = this.generateText(finisher.rank === 1 ? 'ST' : 'ND'.toString(), 34, 'left', finisher.player.color)
169+
rankLetter.position.x = 184
170+
rankLetter.position.y = 32
171+
avatarContainer.addChild(rankLetter)
172+
173+
var hudAvatar = new PIXI.Container()
174+
hudAvatar.addChild(avatar)
175+
176+
avatarContainer.addChild(hudAvatar)
177+
178+
var name = this.generateText(finisher.player.name.toUpperCase(), 50, 'left', finisher.player.color)
179+
var scoreLabel = this.generateText(((finisher.score >= 0) ? finisher.score.toString() + ' points' : '-'), 64, 'left', finisher.player.color)
180+
181+
name.x = 330
182+
name.y = -4
183+
scoreLabel.x = 330
184+
scoreLabel.y = 50
185+
186+
layer.addChild(avatarContainer)
187+
layer.addChild(name)
188+
layer.addChild(scoreLabel)
189+
190+
return layer
191+
}
192+
193+
createEndScene () {
194+
var layer = new PIXI.Container()
195+
196+
var background = new PIXI.Graphics()
197+
background.beginFill(0, 0.85)
198+
background.drawRect(0, 0, WIDTH, HEIGHT)
199+
background.endFill()
200+
201+
layer.backgroundRanking = background
202+
203+
let sprite = this.spriteName
204+
var titleRanking
205+
if (PIXI.utils.TextureCache.hasOwnProperty(sprite)) {
206+
titleRanking = new PIXI.Sprite.fromFrame(sprite)
207+
} else {
208+
ErrorLog.push(new MissingImageError(sprite))
209+
titleRanking = new PIXI.Sprite(PIXI.Texture.EMPTY)
210+
}
211+
titleRanking.anchor.x = titleRanking.anchor.y = 0.5
212+
layer.titleRanking = titleRanking
213+
214+
titleRanking.position.x = WIDTH / 2
215+
titleRanking.position.y = 230
216+
217+
var podium = []
218+
for (var i = 0; i < this.globalData.playerCount; ++i) {
219+
podium.push({
220+
score: this.scores[i],
221+
player: this.globalData.players[i],
222+
rank: 0
223+
})
224+
}
225+
podium.sort(function (a, b) {
226+
return b.score - a.score
227+
})
228+
229+
this.finishers = []
230+
var finishers = new PIXI.Container()
231+
var curRank = 1
232+
var elem
233+
for (i = 0; i < podium.length; ++i) {
234+
if (i > 0 && podium[i - 1].score !== podium[i].score) {
235+
curRank++
236+
}
237+
238+
podium[i].rank = curRank
239+
elem = this.createFinisher(podium[i])
240+
finishers.addChild(elem)
241+
this.finishers.push(elem)
242+
}
243+
244+
for (i = 0; i < this.finishers.length; ++i) {
245+
this.finishers[i].position.x = (WIDTH - this.finishers[0].width) / 2
246+
this.finishers[i].position.y = i * 150
247+
}
248+
finishers.y = 400
249+
250+
layer.addChild(background)
251+
layer.addChild(titleRanking)
252+
layer.addChild(finishers)
253+
254+
layer.visible = false
255+
return layer
256+
}
257+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export class MissingImageError extends Error {
2+
constructor (image, cause) {
3+
super('Could not find image: "' + image + '". Make sure it is in your assets folder and is spelled correctly.')
4+
this.image = image
5+
this.cause = cause
6+
this.name = 'MissingImageError'
7+
}
8+
}

0 commit comments

Comments
 (0)