Talking about Color Hit game, Game development, HTML5, Javascript and Phaser.
Do you play games on GAMEE? It’s a free gaming network which offers real money prizes.
I do not play for money at GAMEE, but I like its hyper causal games, and Color Hit is one of them.
It’s a game similar to Knife Hit, which has already been explained in a tutorial series, but rather than simply throwing knives at a target, Color Hit introduces colored sectors and knives.
Obviously, you can only hit target sectors which match with the color of the knife you are throwing, or it’s game over.
Have a try:
Click or tap to throw a knife. Match knife color with target color or it’s game over. Also, do not throw knives over other knives.
Easy and fun, and here is the completely commented source code:
// the game itself
let game;
// global game options
let gameOptions = {
// target radius, in pixels
targetRadius: 200,
// array with all target colors
targetColors: [0xff0000, 0x00ff00, 0x0000ff],
// target Y position, in screen height ratio
targetY: 1 / 4,
// target rotation speed, in degrees per frame
rotationSpeed: 3,
// knife throwing duration, in milliseconds
throwSpeed: 150,
// minimum angle between two knives
minAngle: 15,
// max rotation speed variation, in degrees per frame
rotationVariation: 2,
// interval before next rotation speed variation, in milliseconds
changeTime: 2000,
// maximum rotation speed, in degrees per frame
maxRotationSpeed: 6
}
// once the window loads...
window.onload = function() {
let gameConfig = {
type: Phaser.AUTO,
backgroundColor: 0xeeeeee,
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
parent: "thegame",
width: 750,
height: 1334
},
scene: playGame
}
game = new Phaser.Game(gameConfig);
window.focus();
}
// PlayGame scene
class playGame extends Phaser.Scene {
// constructor
constructor() {
super("PlayGame");
}
// method to be executed when the scene preloads
preload() {
// load the knife
this.load.image("knife", "knife.png");
}
// method to be executed once the scene has been created
create() {
// make a graphic object but don'y add it to the game
let graphics = this.make.graphics({
x: 0,
y: 0,
add: false
});
// angle covered by a slice
this.sliceAngle = 2 * Math.PI / gameOptions.targetColors.length;
// loop through all colors
for(let i = 0; i < gameOptions.targetColors.length; i ++) {
// set fill stile
graphics.fillStyle(gameOptions.targetColors[i], 1);
// draw a target slice
graphics.slice(gameOptions.targetRadius, gameOptions.targetRadius, gameOptions.targetRadius, 2 * Math.PI - this.sliceAngle * i, 2 * Math.PI - this.sliceAngle * (i + 1), true);
// close and fill the path
graphics.fillPath();
}
// generate wheel texture from the graphics
graphics.generateTexture("wheel", gameOptions.targetRadius * 2, gameOptions.targetRadius * 2);
// at the beginning of the game, both current rotation speed and new rotation speed are set to default rotation speed
this.currentRotationSpeed = gameOptions.rotationSpeed;
this.newRotationSpeed = gameOptions.rotationSpeed;
// can the player throw a knife? Yes, at the beginning of the game
this.canThrow = true;
// group to store all rotating knives
this.knifeGroup = this.add.group();
// add the knife
this.knife = this.add.sprite(game.config.width / 2, game.config.height / 5 * 4, "knife");
// random color of the target to hit
this.knife.target = Phaser.Math.Between(0, gameOptions.targetColors.length - 1);
// tint the knife accordingly
this.knife.tint = gameOptions.targetColors[this.knife.target];
// add the target
this.target = this.add.sprite(game.config.width / 2, game.config.height * gameOptions.targetY, "wheel");
// move the target to front
this.target.depth = 1;
// wait for player input to throw a knife
this.input.on("pointerdown", this.throwKnife, this);
// this is how we create a looped timer event
let timedEvent = this.time.addEvent({
// delay, in milliseconds
delay: gameOptions.changeTime,
// callback function
callback: this.changeSpeed,
// callback scope
callbackScope: this,
// the event will repeat endlessly
loop: true
});
}
// method to change the rotation speed of the target
changeSpeed() {
// random number between -gameOptions.rotationVariation and gameOptions.rotationVariation
let variation = Phaser.Math.FloatBetween(-gameOptions.rotationVariation, gameOptions.rotationVariation);
// new rotation speed
this.newRotationSpeed = (this.currentRotationSpeed + variation) * Phaser.Math.RND.sign();
// set new rotation speed limits
this.newRotationSpeed = Phaser.Math.Clamp(this.newRotationSpeed, -gameOptions.maxRotationSpeed, gameOptions.maxRotationSpeed);
}
// method to throw a knife
throwKnife() {
// can the player throw?
if(this.canThrow) {
// player can't throw anymore
this.canThrow = false;
// tween to throw the knife
this.tweens.add({
// add the knife to tween targets
targets: [this.knife],
// y destination
y: this.target.y + this.target.width / 2,
// tween duration
duration: gameOptions.throwSpeed,
// callback scope
callbackScope: this,
// function to be executed once the tween has been completed
onComplete: function(tween) {
// at the moment, this is a legal hit
let legalHit = true;
// if the knife hit the wrong color...
if(Math.floor(Phaser.Math.Angle.Normalize(this.target.rotation - Math.PI / 2) / this.sliceAngle) != this.knife.target) {
// ... not a legal hit
legalHit = false;
}
// if still a legal hit...
else {
// get an array with all rotating knives
let children = this.knifeGroup.getChildren();
// loop through rotating knives
for (let i = 0; i < children.length; i ++) {
// is the knife too close to the i-th knife?
if(Math.abs(Phaser.Math.Angle.ShortestBetween(this.target.angle, children[i].impactAngle)) < gameOptions.minAngle) {
// this is not a legal hit
legalHit = false;
// no need to continue with the loop
break;
}
}
}
// is this a legal hit?
if(legalHit) {
// player can now throw again
this.canThrow = true;
// addi the rotating knife in the same place of the knife just landed on target
let knife = this.add.sprite(this.knife.x, this.knife.y, "knife");
// impactAngle property saves the target angle when the knife hits the target
knife.impactAngle = this.target.angle;
knife.tint = gameOptions.targetColors[this.knife.target];
// add the rotating knife to knifeGroup group
this.knifeGroup.add(knife);
// bring back the knife to its starting position
this.knife.y = game.config.height / 5 * 4;
// set a new random color target
this.knife.target = Phaser.Math.Between(0, gameOptions.targetColors.length - 1);
// tint the knife accordingly
this.knife.tint = gameOptions.targetColors[this.knife.target];
}
// in case this is not a legal hit
else{
// tween to make the knife fall down
this.tweens.add({
// add the knife to tween targets
targets: [this.knife],
// y destination
y: game.config.height + this.knife.height,
// rotation destination, in radians
rotation: 5,
// tween duration
duration: gameOptions.throwSpeed * 4,
// callback scope
callbackScope: this,
// function to be executed once the tween has been completed
onComplete: function(tween) {
// restart the game
this.scene.start("PlayGame")
}
});
}
}
});
}
}
// method to be executed at each frame. Please notice the arguments.
update(time, delta) {
// rotate the target
this.target.angle += this.currentRotationSpeed;
// get an array with all rotating knives
let children = this.knifeGroup.getChildren();
// loop through rotating knives
for (let i = 0; i < children.length; i ++) {
// rotate the knife
children[i].angle += this.currentRotationSpeed;
// turn knife angle in radians
let radians = Phaser.Math.DegToRad(children[i].angle + 90);
// trigonometry to make the knife rotate around target center
children[i].x = this.target.x + (this.target.width / 2) * Math.cos(radians);
children[i].y = this.target.y + (this.target.width / 2) * Math.sin(radians);
}
// adjust current rotation speed using linear interpolation
this.currentRotationSpeed = Phaser.Math.Linear(this.currentRotationSpeed, this.newRotationSpeed, delta / 1000);
}
}
I found it more fun than Knife Hit because of the different colors, maybe we could randomize a bit more sector colors, in a further step, meanwhile download the source code.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.