Add scoring system based on stroke angles
This commit is contained in:
@@ -48,6 +48,7 @@ AFRAME.registerComponent('beat', {
|
||||
this.cutDirection = new THREE.Vector3();
|
||||
this.destroyed = false;
|
||||
this.gravityVelocity = 0;
|
||||
this.hitEventDetail = {};
|
||||
this.hitBoundingBox = new THREE.Box3();
|
||||
this.poolName = undefined;
|
||||
this.returnToPoolTimer = 800;
|
||||
@@ -74,6 +75,8 @@ AFRAME.registerComponent('beat', {
|
||||
|
||||
this.saberColors = {right: 'blue', left: 'red'};
|
||||
|
||||
this.onEndStroke = this.onEndStroke.bind(this);
|
||||
|
||||
this.initBlock();
|
||||
this.initColliders();
|
||||
if (this.data.type === 'mine') {
|
||||
@@ -546,6 +549,7 @@ AFRAME.registerComponent('beat', {
|
||||
},
|
||||
|
||||
checkCollisions: function () {
|
||||
const cutDirection = this.data.cutDirection;
|
||||
const saberColors = this.saberColors;
|
||||
const saberEls = this.saberEls;
|
||||
const hitBoundingBox = this.hitColliderEl && this.hitBoundingBox.setFromObject(
|
||||
@@ -555,15 +559,29 @@ AFRAME.registerComponent('beat', {
|
||||
|
||||
for (let i = 0; i < saberEls.length; i++) {
|
||||
let saberBoundingBox = saberEls[i].components['saber-controls'].boundingBox;
|
||||
let saberControls;
|
||||
let maxAngle;
|
||||
|
||||
if (!saberBoundingBox) { break; }
|
||||
|
||||
const hand = saberEls[i].getAttribute('saber-controls').hand;
|
||||
if (hitBoundingBox && saberBoundingBox.intersectsBox(hitBoundingBox)) {
|
||||
if (saberEls[i].components['saber-controls'].swinging &&
|
||||
this.data.color === saberColors[hand]) {
|
||||
this.el.emit('beathit', null, true);
|
||||
this.el.emit(`beathit${hand}`, null, true);
|
||||
if (saberEls[i].components['saber-controls'].swinging && this.data.color === saberColors[hand]) {
|
||||
saberControls = saberEls[i].components['saber-controls'];
|
||||
this.hitHand = hand;
|
||||
this.hitSaberEl = saberEls[i];
|
||||
this.hitSaberEl.addEventListener('strokeend', this.onEndStroke);
|
||||
if (cutDirection === 'up' || cutDirection === 'down') {
|
||||
maxAngle = saberControls.maxAnglePlaneX;
|
||||
} else if (cutDirection === 'left' || cutDirection === 'right') {
|
||||
maxAngle = saberControls.maxAnglePlaneY;
|
||||
} else {
|
||||
maxAngle = saberControls.maxAnglePlaneXY;
|
||||
}
|
||||
this.angleBeforeHit = maxAngle;
|
||||
saberControls.maxAnglePlaneX = 0;
|
||||
saberControls.maxAnglePlaneY = 0;
|
||||
saberControls.maxAnglePlaneXY = 0;
|
||||
} else {
|
||||
this.wrongHit(hand);
|
||||
}
|
||||
@@ -590,8 +608,15 @@ AFRAME.registerComponent('beat', {
|
||||
|
||||
if (this.data.type === 'dot' && saberEls[i].components['saber-controls'].swinging &&
|
||||
this.data.color === saberColors[hand]) {
|
||||
this.el.emit('beathit', null, true);
|
||||
this.el.emit(`beathit${hand}`, null, true);
|
||||
this.hitSaberEl = saberEls[i];
|
||||
this.hitSaberEl.addEventListener('strokeend', this.onEndStroke);
|
||||
saberControls = saberEls[i].components['saber-controls'];
|
||||
maxAngle = Math.max(saberControls.maxAnglePlaneX, saberControls.maxAnglePlaneY, saberControls.maxAnglePlaneXY);
|
||||
this.hitHand = hand;
|
||||
this.angleBeforeHit = maxAngle;
|
||||
saberControls.maxAnglePlaneX = 0;
|
||||
saberControls.maxAnglePlaneY = 0;
|
||||
saberControls.maxAnglePlaneXY = 0;
|
||||
} else {
|
||||
this.wrongHit(hand);
|
||||
}
|
||||
@@ -600,6 +625,28 @@ AFRAME.registerComponent('beat', {
|
||||
}
|
||||
},
|
||||
|
||||
onEndStroke: function () {
|
||||
var saberControls = this.hitSaberEl.components['saber-controls'];
|
||||
var maxAngle;
|
||||
var cutDirection = this.data.cutDirection;
|
||||
var hitEventDetail = this.hitEventDetail;
|
||||
if (cutDirection === 'up' || cutDirection === 'down') {
|
||||
maxAngle = saberControls.maxAnglePlaneX;
|
||||
} else if (cutDirection === 'left' || cutDirection === 'right') {
|
||||
maxAngle = saberControls.maxAnglePlaneY;
|
||||
} else {
|
||||
maxAngle = saberControls.maxAnglePlaneXY;
|
||||
}
|
||||
hitEventDetail.angleBeforeHit = this.angleBeforeHit * 180 / Math.PI;
|
||||
hitEventDetail.angleAfterHit = maxAngle * 180 / Math.PI;
|
||||
console.log("MAX ANGLE BEORE: " + this.angleBeforeHit * 180 / Math.PI);
|
||||
console.log("MAX ANGLE AFTER: " + maxAngle * 180 / Math.PI);
|
||||
this.hitSaberEl.removeEventListener('strokeend', this.onEndStroke);
|
||||
|
||||
this.el.emit('beathit', hitEventDetail, true);
|
||||
this.el.emit(`beathit${this.hitHand}`, null, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroyed animation.
|
||||
*/
|
||||
|
||||
@@ -17,7 +17,12 @@ AFRAME.registerComponent('saber-controls', {
|
||||
this.boundingBox = new THREE.Box3();
|
||||
this.bladeEl = el.querySelector('.blade');
|
||||
this.controllerType = '';
|
||||
this.xPlaneNormal = new THREE.Vector3(0, 1, 0);
|
||||
this.yPlaneNormal = new THREE.Vector3(1, 0, 0);
|
||||
this.xyPlaneNormal = new THREE.Vector3(1, 1, 0);
|
||||
this.bladeTipPosition = new THREE.Vector3();
|
||||
this.bladePosition = new THREE.Vector3();
|
||||
this.bladeVector = new THREE.Vector3();
|
||||
this.bladeTipPreviousPosition = new THREE.Vector3();
|
||||
this.saberPosition = new THREE.Vector3();
|
||||
this.swinging = false;
|
||||
@@ -57,13 +62,19 @@ AFRAME.registerComponent('saber-controls', {
|
||||
var startSpeed;
|
||||
var strokeMinSpeed = this.swinging ? startSpeed : this.data.strokeMinSpeed;
|
||||
|
||||
if (this.data.hand === 'left') { return; }
|
||||
|
||||
// Tip of the blade position in world coordinates.
|
||||
this.bladeTipPosition.set(0, 0.9, 0);
|
||||
this.bladePosition.set(0, 0, 0);
|
||||
bladeObject = this.el.object3D;
|
||||
bladeObject.parent.updateMatrixWorld();
|
||||
bladeObject.localToWorld(this.bladeTipPosition);
|
||||
bladeObject.localToWorld(this.bladePosition);
|
||||
|
||||
// Angles between saber and major planes.
|
||||
this.bladeVector.copy(this.bladeTipPosition).sub(this.bladePosition).normalize();
|
||||
var anglePlaneX = this.bladeVector.angleTo(this.xPlaneNormal);
|
||||
var anglePlaneY = this.bladeVector.angleTo(this.yPlaneNormal);
|
||||
var anglePlaneXY = this.bladeVector.angleTo(this.xyPlaneNormal);
|
||||
|
||||
// Distance covered but the saber tip in one frame.
|
||||
distance = this.bladeTipPreviousPosition.sub(this.bladeTipPosition).length() * 1000000;
|
||||
@@ -75,12 +86,14 @@ AFRAME.registerComponent('saber-controls', {
|
||||
this.distanceSamples.push(distance);
|
||||
this.accumulatedDistance += distance;
|
||||
|
||||
// Filter out saber movements that are too slow.
|
||||
// Too slow or small angle is considered wrong hit.
|
||||
// Filter out saber movements that are too slow. Too slow or small angle is considered wrong hit.
|
||||
// Cap stroke to 180 degrees.
|
||||
if (this.accumulatedDistance > this.data.strokeMinSpeed && this.strokeAngle < 180) {
|
||||
// Calculate angle covered by the saber in one frame.
|
||||
// Calculate angle covered by the saber in one frame and accumulate.
|
||||
// Trig: Triangle formed by the saber and the linear distance covered by its tip
|
||||
// Arcsin((distanceCoveredByTip / 2.0) / Length of the blade)
|
||||
// Arcsin((distanceCoveredByTip / 2.0) / Length of the blade) * 2
|
||||
// This is not exact becasuse assumes wrist as pivot point, ignoring elbow and shoulder.
|
||||
// It seems to work well in practice but might need tweaking.
|
||||
this.strokeAngle += ((Math.asin(((distance / 1000000) / 2.0) / 0.9)) /
|
||||
(2 * Math.PI)) * 360 * 2;
|
||||
// Saber has to move more than strokeMinAngle to consider a swing.
|
||||
@@ -88,11 +101,18 @@ AFRAME.registerComponent('saber-controls', {
|
||||
if (!this.swinging && this.strokeAngle > data.strokeMinAngle) {
|
||||
startSpeed = this.accumulatedDistance;
|
||||
this.swinging = true;
|
||||
this.maxAnglePlaneX = 0;
|
||||
this.maxAnglePlaneY = 0;
|
||||
this.maxAnglePlaneXY = 0;
|
||||
}
|
||||
this.maxAnglePlaneX = anglePlaneX > this.maxAnglePlaneX ? anglePlaneX : this.maxAnglePlaneX;
|
||||
this.maxAnglePlaneY = anglePlaneY > this.maxAnglePlaneY ? anglePlaneY : this.maxAnglePlaneY;
|
||||
this.maxAnglePlaneXY = anglePlaneXY > this.maxAnglePlaneXY ? anglePlaneXY : this.maxAnglePlaneXY;
|
||||
} else {
|
||||
// Stroke finishes. Reset swinging state.
|
||||
if (this.swinging) {
|
||||
console.log("Angle " + this.strokeAngle);
|
||||
// console.log("MaxAngle " + this.maxAnglePlaneX * 180 / Math.PI);
|
||||
this.el.emit('strokeend');
|
||||
this.swinging = false;
|
||||
this.strokeAngle = 0;
|
||||
this.accumulatedDistance = 0;
|
||||
|
||||
@@ -101,19 +101,25 @@ AFRAME.registerState({
|
||||
localStorage.setItem('activeHand', state.activeHand);
|
||||
},
|
||||
|
||||
beathit: state => {
|
||||
beathit: (state, payload) => {
|
||||
var score = 0;
|
||||
if (state.damage > DAMAGE_DECAY) {
|
||||
state.damage -= DAMAGE_DECAY;
|
||||
}
|
||||
state.score.beatsHit++;
|
||||
state.score.score += 1000;
|
||||
state.score.combo++;
|
||||
if (state.score.combo > state.score.maxCombo) {
|
||||
state.score.maxCombo = state.score.combo;
|
||||
}
|
||||
|
||||
score += payload.angleBeforeHit >= 90 ? 70 : (payload.angleBeforeHit / 90) * 70;
|
||||
score += payload.angleAfterHit >= 60 ? 30 : (payload.angleAfterHit / 60) * 30;
|
||||
state.score.score += Math.floor(score * state.score.multiplier);
|
||||
|
||||
state.score.multiplier = state.score.combo >= 8
|
||||
? 8
|
||||
: 2 * Math.floor(Math.log2(state.score.combo));
|
||||
// console.log("BEAT SCORE: " + score);
|
||||
},
|
||||
|
||||
beatmiss: state => {
|
||||
|
||||
Reference in New Issue
Block a user