VRampage.com (Multiplayer First-person shooter)

,

Hello,

This is my first announcement of my new project VRampage :crossed_swords:
It’s a multiplayer first-person shooter, with a goal to be cross-platform across mobile, desktop and VR.

It’s powered by Babylon.js and Colyseus, it is also my first Typescript project.

Backstory
I have used Unity for many years, but decided to abandon it when they introduced the runtime fee.
After that I started looking for an open-source engine. The other candidates were Godot, Aframe and Three.js. I saw the potential and feel in love with Babylon.js.

Now I have dedicated myself to this engine and plan on using it to build my own game engine (Reboot) on top, and try my luck as a independent game developer with one more startup, which this time will be called VRooster.com

Features
The game runs a classic FFA game mode, where each player have 100 HP and can be shot and killed. When killed the players will respawn.

NPCs spawns and will chase down the player and shoot at them.

The game works on Desktop and VR (Only tested Oculus 2)

Try it at vrampage.com :joystick:

Controls

  • Left Mouse - Fire
  • Q - Select/Unselect Entity
  • CTRL - Change Gizmo for selected entity
  • T - Toggle Menu
  • 1-2 - Change Weapon

Have fun :blush:
Please leave any type of feedback (even the bad). Thanks :heart:

9 Likes

Man! what a good way to start the week!!

Keep up the good work!

1 Like

Finally I reached a state where I wanted to share some progress of my project.
The newest version is 0.0.112, and the game now is available at vrampage.com.

Main new features/changes:

  • The map (Stranded), filled with beautiful FREE assets from https://quaternius.com/
  • Spawn component - for having multiple player spawn locations
  • Behavior component - Used for running behavior trees (Currently hostile-npc and hostile-npc-spawner)
  • Prefabs support (WIP) - Support for defining prefabs for easier spawning in-game and reducing the size of the map json.
  • The game now runs properly on VR again (Only tested oculus)

Known issues:

  • Everything else than terrain is not used for navigation mesh
  • The physics aggregates (used for players, npcs, projectiles) will loose their correct position when colliding with other physics aggregates. This can be seen by toggling the debug mode on through the quick menu, which can be activated with 5.
  • Performance is not great on VR. Implementing object pools and caching has not been a priority.
  • Entities spawned as prefabs during map startup does not always gets loaded correctly (etc. missing meshes)

Prefab example
So far one of the primary goals with the engine is to have as much as possible defined in JSON. This is the JSON content for npc_robot:

{
  "id": "npc_robot",
  "components": [
    {
      "type": "mesh_rendering",
      "meshes": [
        {
          "type": "model",
          "modelPath": "player/robot/robot.glb",
          "scale": {
            "x": 4.25,
            "y": 4.25,
            "z": 4.25
          },
          "rotation": {
            "x": 0,
            "y": 0,
            "z": 0
          },
          "position": {
            "x": 0,
            "y": 0,
            "z": 0
          }
        }
      ]
    },
    {
      "type": "collider",
      "colliders": [
        {
          "type": "cylinder",
          "position": {
            "x": 0,
            "y": -4,
            "z": 0
          },
          "radius": 5,
          "height": 4
        }
      ]
    },
    {
      "type": "animation_controller",
      "animations": [
        {
          "name": "walk",
          "path": "avatar/rifle_walk.glb",
          "speed": 1,
          "loop": true
        }
      ]
    },
    {
      "type": "transform",
      "position": {
        "x": 0,
        "y": 0,
        "z": 0
      },
      "scale": {
        "x": 1,
        "y": 1,
        "z": 1
      },
      "rotation": {
        "x": 0,
        "y": 0,
        "z": 0
      }
    },
    {
      "type": "interactive"
    },
    {
      "type": "ui",
      "elements": [
        {
          "type": "text",
          "text": "NPC Robot",
          "fontSize": "128",
          "color": "red"
        }
      ]
    },
    {
      "type": "physics"
    },
    {
      "type": "behavior",
      "tree": "hostile-npc"
    },
    {
      "type": "agent"
    },
    {
      "type": "health",
      "health": 100
    },
    {
      "type": "owner",
      "id": ""
    }
  ]
}

That’s it for now :blush:

1 Like

Version 0.0.123


Been working on the UI/Menu, attempting to follow the Model–view–controller (MVC) pattern.
Dynamic text now gets updated from the View Controller, like this implementation in the heads-up-display-view:

this.controller.state
      .getProperty<string>("fps")
      .onValueChangedObservable.add((value) => {
        this.fpsText.text = value;
      });

The main view can be toggled with T

Reworked my projectile (Previously it was implemented using a velocity-component, consisting of a multiplier and a direction that updated the transform-component position).
Now it’s implemented as a behavior tree (like the hostile-npc-spawner and hostile-npc), which made it easy to create a new type of a projectile. This is the rocket behavior tree:

{
    "type": "sequence",
    "children": [
        {
            "type": "velocity",
            "multiplier": 70
        },
        {
            "type": "delay",
            "delay": 0.15,
            "blackboardKey": "delay"
          },
          {
            "type": "spawn-entity",
            "prefab": "smoke_trail_fx"
          }
    ]
  }

I made it possible to change between 2 weapons to try out the different projectiles. The weapon can be switched with 1 - 2

Players and NPCs now have different models.

The main focus has been PC. The game works on VR, but the features are limited compared to the PC version.

The issues mentioned in the previous post is still not fixed.

2 Likes

0.0.124

Added simple weapon bobbing to keep the scene feeling alive and engaging.
vrampage-weapon-bobbing

export class WeaponBobbing {
  game: GameController;
  playerTransform: TransformComponent;
  weaponTransform: TransformNode;

  private idleAmplitude: Vector3 = new Vector3(0.002, 0.002, 0); // Amplitude for idle bobbing
  private moveAmplitude: Vector3 = new Vector3(0.015, 0.015, 0);
  private idleFrequency: number = 2; // Frequency of the sine wave
  private moveFrequency: number = 5;

  private lastPlayerPosition: Vector3 = Vector3.Zero(); // Tracks player position for velocity calculation
  private elapsedTime: number = 0; // Elapsed time since the start of the game

  constructor(game: GameController) {
    this.game = game;
  }

  initialize(weaponTransform: TransformNode) {
    this.playerTransform =
      this.game.entityController.localPlayerEntity.getComponent<TransformComponent>(
        ComponentType.TRANSFORM,
      );
    this.weaponTransform = weaponTransform;
    this.lastPlayerPosition.copyFrom(this.playerTransform.position);
  }
  update(deltaTime: number) {
    if (!this.playerTransform || !this.weaponTransform) {
      return;
    }

    // Update elapsed time
    this.elapsedTime += deltaTime;

    // Calculate the player's velocity
    const currentPosition = this.playerTransform.position;
    const velocity = currentPosition.subtract(this.lastPlayerPosition);
    this.lastPlayerPosition.copyFrom(currentPosition);

    // Determine bobbing amplitude based on velocity
    const isMoving = velocity.length() > 0.001;
    const amplitude = isMoving ? this.moveAmplitude : this.idleAmplitude;
    const frequency = isMoving ? this.moveFrequency : this.idleFrequency;

    // Calculate bobbing offsets using sine and cosine for smooth oscillation
    const xOffset = amplitude.x * Math.sin(this.elapsedTime * frequency);
    const yOffset = amplitude.y * Math.cos(this.elapsedTime * frequency);

    // Apply the calculated offsets to the weapon's position
    this.weaponTransform.position.addInPlace(new Vector3(xOffset, yOffset, 0));

    // Optional: Dampen the weapon's position slightly to avoid excessive accumulation
    this.weaponTransform.position = Vector3.Lerp(
      this.weaponTransform.position,
      Vector3.Zero(),
      0.1,
    );
  }
}
3 Likes

Looking good!

Weapon bobbing will never not remind me of my beloved HEXEN :heart:

1 Like