Improve gameplay and design

This commit is contained in:
schmelczerandras 2020-10-22 21:50:39 +02:00
parent af616042f3
commit 1d1bc6655c
15 changed files with 163 additions and 74 deletions

View file

@ -155,20 +155,13 @@ export class PlayerCharacterPhysical
return;
}
const start = vec2.clone(this.center);
const direction = vec2.subtract(vec2.create(), position, start);
const direction = vec2.subtract(vec2.create(), position, this.center);
vec2.normalize(direction, direction);
vec2.add(
start,
start,
vec2.scale(vec2.create(), direction, settings.projectileStartOffset),
);
const velocity = vec2.scale(direction, direction, settings.projectileSpeed);
vec2.add(velocity, velocity, this.velocity);
const strength = this.projectileStrength / 2;
this.projectileStrength -= strength;
const projectile = new ProjectilePhysical(
start,
vec2.clone(this.center),
20,
this.colorIndex,
strength,
@ -192,10 +185,6 @@ export class PlayerCharacterPhysical
return this.head.center;
}
public get velocity(): vec2 {
return this.head.velocity;
}
public distance(target: vec2): number {
return (
Math.min(
@ -272,17 +261,18 @@ export class PlayerCharacterPhysical
} else {
const leftFootGravity = this.currentPlanet!.getForce(this.leftFoot.center);
const rightFootGravity = this.currentPlanet!.getForce(this.rightFoot.center);
if (movementForce.y > settings.maxAcceleration / 4) {
if (this.lastMovementWasRelative) {
vec2.rotate(movementForce, movementForce, vec2.create(), this.direction);
}
if (vec2.dot(movementForce, actualGravity) < -vec2.length(movementForce) * 0.8) {
vec2.scale(leftFootGravity, leftFootGravity, 0.35);
vec2.scale(rightFootGravity, rightFootGravity, 0.35);
}
this.applyForce(this.leftFoot, leftFootGravity, deltaTime);
this.applyForce(this.rightFoot, rightFootGravity, deltaTime);
if (this.lastMovementWasRelative) {
vec2.rotate(movementForce, movementForce, vec2.create(), this.direction);
}
const headGravity = this.currentPlanet!.getForce(this.head.center);
if (vec2.length(headGravity) < vec2.length(actualGravity) / 2) {

View file

@ -16,6 +16,7 @@ import { ReactsToCollision } from '../physics/physicals/reacts-to-collision';
import { UpdateObjectMessage } from 'shared/lib/src/objects/update-object-message';
import { UpdateGameObjectMessage } from '../update-game-object-message';
import { PlayerCharacterPhysical } from './player-character-physical';
import { moveCircle } from '../physics/functions/move-circle';
@serializesTo(ProjectileBase)
export class ProjectilePhysical
@ -41,6 +42,26 @@ export class ProjectilePhysical
) {
super(id(), center, radius, colorIndex, strength);
this.object = new CirclePhysical(center, radius, this, container, 0.9);
this.moveOutsideOfObject();
}
private moveOutsideOfObject() {
let wasCollision = true;
const delta = vec2.scale(
vec2.create(),
vec2.normalize(vec2.create(), this.velocity),
10,
);
while (wasCollision) {
const intersecting = this.container
.findIntersecting(this.boundingBox)
.filter((g) => g instanceof PlayerCharacterPhysical && g.team === this.team);
const { hitSurface } = moveCircle(this.object, delta, intersecting, true);
wasCollision = hitSurface;
}
vec2.add(this.center, this.center, delta);
vec2.add(this.center, this.center, delta);
}
public calculateUpdates(): UpdateObjectMessage {

View file

@ -4,4 +4,4 @@ import { PhysicalBase } from '../physicals/physical-base';
export const evaluateSdf = (target: vec2, objects: Array<PhysicalBase>) =>
objects
.filter((i) => i.canCollide)
.reduce((min, i) => (min = Math.min(min, i.distance(target))), 1000);
.reduce((min, i) => (min = Math.min(min, i.distance(target))), 1000000);

View file

@ -9,6 +9,7 @@ export const moveCircle = (
circle: CirclePhysical,
delta: vec2,
possibleIntersectors: Array<Physical>,
ignoreCollision = false,
): {
realDelta: vec2;
hitSurface: boolean;
@ -38,6 +39,9 @@ export const moveCircle = (
(i) => i.distance(nextCircle.center) <= circle.radius,
)!;
if (ignoreCollision) {
circle.center = vec2.add(circle.center, circle.center, delta);
} else {
if (reactsToCollision(intersecting)) {
intersecting.onCollision(circle.gameObject);
}
@ -45,6 +49,7 @@ export const moveCircle = (
if (reactsToCollision(circle)) {
circle.onCollision(intersecting.gameObject);
}
}
const dx =
evaluateSdf(vec2.add(vec2.create(), nextCircle.center, vec2.fromValues(0.01, 0)), [

View file

@ -28,7 +28,6 @@ import { PhysicalContainer } from '../physics/containers/physical-container';
import { getBoundingBoxOfCircle } from '../physics/functions/get-bounding-box-of-circle';
import { isCircleIntersecting } from '../physics/functions/is-circle-intersecting';
import { PlayerCharacterPhysical } from '../objects/player-character-physical';
import { Physical } from '../physics/physicals/physical';
import { freeTeam, requestTeam } from './player-team-service';
import { PlanetPhysical } from '../objects/planet-physical';

View file

@ -50,25 +50,43 @@
<section id="settings">
<label for="enable-relative-movement">
<input id="enable-relative-movement" type="checkbox" />
<img alt="a dashed circle" src="../static/circle.svg" />
<img
title="Make movement be relative to the gravitational field"
alt="a dashed circle"
src="../static/circle.svg"
/>
</label>
<label for="enable-vibration">
<input id="enable-vibration" type="checkbox" />
<img alt="vibrating mobile" src="../static/vibrate.svg" />
<img
title="Enable vibration on mobile"
alt="vibrating mobile"
src="../static/vibrate.svg"
/>
</label>
<label for="enable-sounds">
<input id="enable-sounds" type="checkbox" />
<img alt="speaker with sound waves" src="../static/volume.svg" />
<img
title="Enable sounds"
alt="speaker with sound waves"
src="../static/volume.svg"
/>
</label>
<img id="logout" alt="logout" src="../static/logout.svg" />
<img
title="Exit from current game"
id="logout"
alt="logout"
src="../static/logout.svg"
/>
</section>
</div>
<div id="toggle-settings-container">
<img
title="Toggle settings"
id="toggle-settings"
class="icon"
alt="toggle-settings"

View file

@ -94,21 +94,58 @@ body {
}
.player-tag {
font-size: 1.3rem;
position: absolute;
transform: translateX(-50%) translateY(-50%) rotate(-15deg);
transition: left 200ms, top 200ms;
border-radius: 1000px;
&.decla {
color: $bright-decla;
div {
height: 3px;
background-color: rebeccapurple;
background-color: $bright-decla;
&:before {
background-color: $bright-decla;
opacity: 0.3;
}
}
}
&.red {
color: $bright-red;
div {
background-color: $bright-red;
&:before {
background-color: $bright-red;
opacity: 0.4;
}
}
}
div {
position: relative;
height: 5px;
border-radius: 1000px;
&:before {
content: '';
position: absolute;
height: 5px;
width: 50px;
box-sizing: border-box;
border-radius: 1000px;
}
}
}
.ownership {
font-size: 1.3rem;
position: absolute;
font-size: 0;
transform: translateX(-50%) translateY(-50%);
@include square(50px);
border-radius: 1000px;
mask: url('../static/mask.svg');
}
.planet-progress {
@ -122,21 +159,24 @@ body {
height: $height;
border-radius: 4px;
z-index: 100;
div {
height: $height;
}
div:nth-child(1) {
background: rebeccapurple;
border-radius: 100px 0 0 100px;
background: $bright-decla;
}
div:nth-child(2) {
background: gray;
background: $bright-neutral;
}
div:nth-child(3) {
background: red;
border-radius: 0 100px 100px 0;
background: $bright-red;
}
}
}

View file

@ -2,7 +2,6 @@ import { vec2 } from 'gl-matrix';
import {
CircleLight,
ColorfulCircle,
compile,
FilteringOptions,
Flashlight,
Renderer,
@ -17,7 +16,6 @@ import {
settings,
TransportEvents,
SetAspectRatioActionCommand,
rgb,
PlayerInformation,
PlayerDiedCommand,
UpdatePlanetOwnershipCommand,
@ -66,6 +64,8 @@ export class Game {
this.socket.io.opts.transports = ['polling', 'websocket'];
});
this.socket.on('disconnect', this.destroy.bind(this));
this.socket.on(TransportEvents.ServerToPlayer, (serialized: string) => {
const command = deserialize(serialized);
if (command instanceof PlayerDiedCommand) {
@ -145,7 +145,6 @@ export class Game {
//enableStopwatch: true,
},
{
ambientLight: rgb(0.45, 0.4, 0.45),
colorPalette: settings.palette,
enableHighDpiRendering: true,
lightCutoffDistance: settings.lightCutoffDistance,

View file

@ -1,6 +1,6 @@
import { vec2 } from 'gl-matrix';
import { Renderer } from 'sdf-2d';
import { calculateViewArea, GameObject, mixRgb, settings, UpdateMessage } from 'shared';
import { calculateViewArea, GameObject, mixRgb, settings } from 'shared';
import { Game } from '../game';
import { ViewObject } from './view-object';
@ -32,8 +32,7 @@ export class Camera extends GameObject implements ViewObject {
ambientLight: mixRgb(
settings.backgroundGradient[0],
settings.backgroundGradient[1],
(this.center.x - settings.worldLeftEdge) /
(Math.abs(settings.worldLeftEdge) + Math.abs(settings.worldRightEdge)),
vec2.length(this.center) / settings.worldRadius,
),
});
}

View file

@ -1,13 +1,6 @@
import { vec2 } from 'gl-matrix';
import { Drawable, Renderer } from 'sdf-2d';
import {
CommandExecutors,
Id,
Random,
PlanetBase,
UpdateMessage,
settings,
} from 'shared';
import { Renderer } from 'sdf-2d';
import { CommandExecutors, Id, Random, PlanetBase } from 'shared';
import { RenderCommand } from '../commands/types/render';
import { PlanetShape } from '../shapes/planet-shape';
import { ViewObject } from './view-object';
@ -32,18 +25,19 @@ export class PlanetView extends PlanetBase implements ViewObject {
public step(deltaTimeInMilliseconds: number): void {
this.shape.randomOffset += deltaTimeInMilliseconds / 4000;
this.shape.colorMixQ = this.ownership;
let teamName = 'Neutral';
if (this.ownership < 0.5 - settings.planetControlThreshold) {
teamName = 'Decla';
} else if (this.ownership > 0.5 + settings.planetControlThreshold) {
teamName = 'Red';
}
this.ownershipProgess.innerText = `${teamName} ${Math.round(
(Math.abs(this.ownership - 0.5) / 0.5) * 100,
)}%`;
private getGradient(): string {
const sideDecla = this.ownership < 0.5;
const sidePercent = (Math.abs(this.ownership - 0.5) / 0.5) * 100;
const color = sideDecla ? 'var(--bright-decla)' : 'var(--bright-red)';
return `conic-gradient(
${color} ${sidePercent}%,
${color} ${sidePercent}%,
var(--bright-neutral) ${sidePercent}%,
var(--bright-neutral) 100%
)`;
}
public beforeDestroy(): void {
this.ownershipProgess.parentElement?.removeChild(this.ownershipProgess);
}
@ -56,6 +50,7 @@ export class PlanetView extends PlanetBase implements ViewObject {
const screenPosition = renderer.worldToDisplayCoordinates(this.center);
this.ownershipProgess.style.left = screenPosition.x + 'px';
this.ownershipProgess.style.top = screenPosition.y + 'px';
this.ownershipProgess.style.background = this.getGradient();
renderer.addDrawable(this.shape);
}

View file

@ -1,13 +1,6 @@
import { vec2 } from 'gl-matrix';
import { Renderer } from 'sdf-2d';
import {
Circle,
Id,
PlayerCharacterBase,
UpdateMessage,
CharacterTeam,
settings,
} from 'shared';
import { Circle, Id, PlayerCharacterBase, CharacterTeam, settings } from 'shared';
import { OptionsHandler } from '../options-handler';
import { BlobShape } from '../shapes/blob-shape';
@ -35,7 +28,7 @@ export class PlayerCharacterView extends PlayerCharacterBase implements ViewObje
this.previousHealth = this.health;
this.nameElement = document.createElement('div');
this.nameElement.className = 'player-tag';
this.nameElement.className = 'player-tag ' + this.team;
this.nameElement.innerText = this.name;
this.healthElement = document.createElement('div');
this.nameElement.appendChild(this.healthElement);
@ -47,7 +40,7 @@ export class PlayerCharacterView extends PlayerCharacterBase implements ViewObje
public step(deltaTimeInMilliseconds: number): void {
this.timeSinceLastNameElementUpdate += deltaTimeInMilliseconds;
this.healthElement.style.width = this.health + '%';
this.healthElement.style.width = (50 * this.health) / settings.playerMaxHealth + 'px';
if (this.previousHealth > this.health) {
this.previousHealth = this.health;
if (OptionsHandler.options.vibrationEnabled) {
@ -89,7 +82,7 @@ export class PlayerCharacterView extends PlayerCharacterBase implements ViewObje
const headFeetDelta = vec2.subtract(footAverage, this.head!.center, footAverage);
vec2.normalize(headFeetDelta, headFeetDelta);
const textOffset = vec2.scale(headFeetDelta, headFeetDelta, this.head!.radius + 50);
const textOffset = vec2.scale(headFeetDelta, headFeetDelta, this.head!.radius + 60);
return vec2.add(textOffset, this.head!.center, textOffset);
}
}

View file

@ -96,7 +96,8 @@ form {
padding: $small-padding;
border: $border;
cursor: pointer;
margin: $border-width-focused - $border-width $small-padding / 2;
margin: $border-width-focused - $border-width $border-width-focused -
$border-width + $small-padding / 2;
}
&:focus {
@ -107,7 +108,7 @@ form {
&:checked + label {
border-color: $accent;
border-width: $border-width-focused;
margin-right: 0 $small-padding / 2;
margin: 0 $small-padding / 2;
}
}
}

View file

@ -8,14 +8,22 @@
justify-content: center;
top: 0;
right: 0;
font-size: 0;
}
#toggle-settings-container {
padding: $medium-padding;
cursor: pointer;
padding: $medium-padding $medium-padding 0 $medium-padding;
@media (max-width: $breakpoint) {
padding: $medium-padding $medium-padding $medium-padding 0;
}
#toggle-settings {
animation: spin 32s linear infinite;
@keyframes spin {
100% {
transform: rotate(360deg);
@ -53,9 +61,11 @@
opacity: 0;
transition: transform $animation-time, opacity $animation-time;
pointer-events: none;
&.open {
transform: none;
opacity: 1;
pointer-events: inherit;
}
img,

View file

@ -13,3 +13,12 @@ $breakpoint: 700px;
$height-breakpoint: 500px;
$large-icon: 48px;
$small-icon: 32px;
$bright-decla: #4069a5;
$bright-red: #d15652;
$bright-neutral: #88888877;
:root {
--bright-decla: #{$bright-decla};
--bright-red: #{$bright-red};
--bright-neutral: #{$bright-neutral};
}

10
frontend/static/mask.svg Normal file
View file

@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="000000" >
<defs>
<mask id="hole">
<rect width="100%" height="100%" fill="white"/>
<circle r="20" cx="50" cy="50" fill="black"/>
</mask>
</defs>
<rect width="100%" height="100%" fill="black" mask="url(#hole)"/>
</svg>

After

Width:  |  Height:  |  Size: 311 B