[OPEN SOURCE] Multiplayer 3D RPG Using Colyseus

@oriongu, it truly has been a joy to watch how this project and your learning have progressed together. I greatly appreciate how people share their journeys with others. I think it’s very inspiring, so thanks for sharing it with us!

As for possible next steps you could take. In today’s world, making a commercialized game is a MASSIVE undertaking. There’s a reason game studios end up with hundreds of people working on a game.

That said, people often forget that indie games are often truly very enjoyable experiences…both for the game developer, as well as the people playing it.

If I could offer one simple idea…no matter what you do with it, build a community around it. Build a fan-base for the content, and a community of people who are interested in its development. Not too dissimilar from what you’ve done here already. In my experience with Babylon, one of the most life-giving and encouraging things we’ve done is to help foster this wonderful community. Seriously, this community has given us ideas, inspiration, passion, perseverance. Everything we’ve needed to continue our own journey.

Now for the specifics of where this game could go? I’d turn it around on you and ask, what is something you’re personally truly passionate about. Is it all about the learning? Is it about the idea of possibly making some money with this? Is it about doing something in collaboration with other people? What is your main goal that you’d personally like to get out of the next phase.

I’ll tell you an idea that @jelster and I have played around with for the past couple years as he was in the process of bringing his book to life.

Imagine a game that was completely open source…so anyone could add new features to the game. The ruleset of the game (the core game logic) existed in the repo. How characters move, how inventory might work, how the point system works, etc.

The content of the game however could be something completely different. Imagine a second repo where people could upload their own assets or game entities…or taking that further…what if a person could upload their own “encounter”…a single file that the core game consumes and then offers to the player.

In this hypothetical world where the game is somewhat separate from the content, you could foster two different creative areas…1, the game itself, 2 the content of the game.

In this hypothetical world, the content would effectively be generated by a community. So you’d maybe start with a basic character and then the game reads the community directory of assets and creates adventures or encounters for your character. Play the game the first time and your character has to fight zombie ice cream cones. Spin up the game a week later and now your character has to barter with cheating scandalous mutant Hot Sauce bottles.

Ok random, I know, but the uber idea is this, you create an open source game framework, and then build it to be modular from a content standpoint. Allow anyone to add their own content (within a specific framework of following the game rules) and create a public github repo that allows anyone to add their own custom content to the game…not just for themselves, but for everyone! Live! So the game is constantly pulling content from this public repo.

Crazy idea, I know. The only piece of advice would be to make sure you moderate the community repo so that no offensive content comes in.

Anyways…hope that gives you something to chew on and think about.

Cheers!

4 Likes

Hi @oriongu ,
in my opinion you should go for the open source option. With open source you could open the door to contributors with the source code. Also in the future you could monetize the project through in-game advertising, youtube, twitch, etc…
As I said before in reference to the “lp-mud” these had the advantage that the game is generated at runtime using lpc code and with this there is a system of users with different permissions (mudlib), an online compiler, browser for the file system (similar to bash) and file transfer (ftp) the generation of scenes is much faster and dynamic.
The idea is to separate the system code and the game system code and scenarios by generating an online programming environment.
Anyway keep up the good work.
Cheers!

1 Like

Many thanks for eveyone who contributed ideas, thoughts & encouregements, it will take me a while to digest all of that.

It seems like the consensus is to keep the project open sourced.

I like the idea of keeping this project open-sourced too so I will keep on working on improving it as I’ve been doing over the last few months. :grinning:

Here is an ambitious roadmap of the things I’d like to achieve & improve over the next 6 months:

  • Basic name, logo & branding
  • Basic levelling up & experience system
  • Improve AI pathfinding & behaviour
    – when in aggro mode, make sure AI does not go though walls
    – spawn and patrol around an area only
  • Better monster and player selection (its a little hit & miss atm)
  • UI framework to deal with popups/panels using BABYLON GUI
    – Inventory Panel
    – Character Stats Panel
    – Skills Panel
  • Item drops from monsters (the actual items & gold will be on the floor ready for anyone to pickup so be quick)
  • Ability for players to pickup to inventory & drop items from inventory
  • Items & Spells database ( to be stored in a json file, format to be determined and associated ui tooltip to display information )
  • NPC vendors where you can buy spells & items

If & when I manage to finish the above, I will do another retrospective, and see where to go from there,

Orion

3 Likes

Hi there,

Not much since last post, I’ve been working on some basic inventory ui framework, levelling system, and some bug fixing.

I’ve also started working on a decent ability structure and came up with the below, I’m sure I’m missing some stuff but it’s a good start and should allow to get most spells done.

More details here for those interested : t5c/Abilities.ts at main · oriongunning/t5c · GitHub

Below is an example of a heal spell:

{
  name: "Heal",
  key: "heal",
  icon: "./icons/ABILITY_heal.png",
  description: "A spell from ancient times that will leave target feeling fresh & revigorated.",
  castSelf: true,
  castTime: 1000,
  cooldown: 3000, // 1 seconds
  repeat: 0,
  repeatInterval: 0;
  range: 0,
  effect: {
      type: 'self', 
      effect: 'fireball', 
      color: 'white' 
  },
  casterPropertyAffected: {
      'mana': -40,
  },
  targetPropertyAffected: {
      'health': 50,
  },
  requiredToLearn: {
      'level': 4,
      'intelligence': 18,
      'wisdom': 24,
  },
}

Now to implement it into the project!

5 Likes

Hi,
Hope you are well. I keep being amazed with the progress you make on this project, with all the features and good stuff and now, with your committment to keep this open source. I was just looking at your roadmap and the comments from the Team and Community members… and I was just wondering: What do you expect? I mean, people are always very willing to tell you that it should be open source, that they will eventually engage or commit, but then… how is it in real life? Of course, managing a community is less work (troubles and risks) than managing employees but then, let’s face it, it still needs strong community management, daily monitoring and moderating or it won’t go anywhere. Are you really willing to put all of that on yourself… and for what kind of return? Just wondering and my opinion only (you don’t have to answer).

1 Like

I hear you @mawa, but don’t overthink it :slight_smile: I’m not planning to create any more community than what we already have here, as I clearly do not have the time yet do deal with anything more than that. For the moment I’m just happy to put more time into this project just for the sake of it and for whoever it can help/inspire/interest.

On a side note, I will be organizing a small stress test to see if we can crash the server(I have no doubt we will) (10people would be ideal) :stuck_out_tongue: , stay tuned.

4 Likes

Awesome spirit and totally in line with this Community, I believe. Can’t do anything more than acknowledge and applause :clap: Else, I guess I already told you that your work is both inspiring :smiling_face_with_three_hearts: and… frustrating :thinking: :wink:… The frustration part of course coming from the fact that I just don’t seem to be able to keep with your pace :grin:

1 Like

Love seeing the progress and the visual style, it looks like dnd miniatures come to life :star_struck:

2 Likes

thanks @carolhmj

I just spent a bit of time reorganizing the UI, it’s looking quite neat now :slight_smile:

And yes, player levelling and experience is fully integrated :slight_smile:

4 Likes

Been working on a better level design. I’m now able to use unity terrain in my workflow without increasing the level size too much. (currently 7mo)

But I need better lighting.


And 2 videos for the fun


3 Likes

added ping, entity count and fps back

4 Likes

you are unstoppable !!!

1 Like

woooooo getting cooler and cooler!

1 Like

@carolhmj
@sebavan
Thanks guys,

Woohoo! That was a tough one but I managed to get proper casting and cooldown implemented for all abilities. All controlled by the server. I could optimize it by doing some reconciliation on the client but I’m quite happy with it at the moment (timing is slightly off due to latency). Now to do some refactoring to tidy it all up.

Unfortunately, demo is running really slow as my terrain and water material seem to be making FPS drop to 20 :frowning: (if I disable them, fps jumps back to 60). That’ll depend on you computer of course, but there is an issue there. Any ideas?

2 Likes

Try to open the inspector and see what’s wrong (draw calls, gpu time or smth else).

1 Like

The player is not moving, I try it using arrow keys and WASD too.

1 Like

Hi there. Welcome to babylon.js community. You need to use your mouse (similar to diablo 3), keep pressing the left click and drag your mouse around to move your character.

I may add keyboard movement, but for the moment it is not planned.

I’ve added the inspector back to the demo to help debugging if you want to have a look? :slight_smile: :grin: https://t5c.onrender.com/

Disabling render targets (whatever that is) also brings fps back to 60… Damn I’m just gonna have to pause coding and focus on performance for a while. And understanding the engine a little better.

1 Like

Hi there,

Quick progress update:

  • lots of minor refactoring, duplication removal and performance improvements
  • added a better assets loading system (shows loading progress)
  • added an animation to the ability cooldown
  • players can now be selected too
  • logo placeholder
  • player notifications in chat
  • dot (damage over time) ability is now fully implemented woot!
  • guest login
  • level & experience are saved to DB now

player notifications

game loading progress

cooldown animated (hard to see on the video but it’s cool)

4 Likes

Hi there,

I think I finally cracked loading assets (found something online that pointed me in the right direction) using the assets manager and thougth I should share and avoid someone else the struggle (it would have helped me greatly if something like this was in the documentation in the asset manager section):

let assetsToLoad = [

    // sounds
    { name: "enemy_attack_1", filename: "enemy_attack_1.wav", extension: "wav" }, 
    { name: "enemy_attack_2", filename: "enemy_attack_2.wav", extension: "wav" }, 
    { name: "fire_attack_1", filename: "fire_attack_1.wav", extension: "wav" }, 
    { name: "fire_attack_2", filename: "fire_attack_2.wav", extension: "wav" }, 
    { name: "heal_1", filename: "heal_1.wav", extension: "wav" }, 
    { name: "music", filename: "music.mp3", extension: "mp3" }, 
    { name: "player_walking", filename: "player_walking.wav", extension: "wav" }, 

    // models
    { name: "player_hobbit", filename: "player_hobbit.glb", extension: "glb", instantiate: true }, 
    { name: "monster_unicorn", filename: "monster_unicorn.glb", extension: "glb", instantiate: true  }, 
    { name: "monster_bear", filename: "monster_bear.glb", extension: "glb", instantiate: true }, 

    // image
    { name: "ABILITY_base_attack", filename: "icons/ABILITY_base_attack.png", extension: "png", type: "image" },
    { name: "ABILITY_fireball", filename: "icons/ABILITY_fireball.png", extension: "png", type: "image" },
    { name: "ABILITY_poisonball", filename: "icons/ABILITY_poisonball.png", extension: "png", type: "image"},
    { name: "ABILITY_heal", filename: "icons/ABILITY_heal.png", extension: "png", type: "image"},

    // textures
    { name: "selected_circle_green", filename: "selected_circle_green.png", extension: "png", type: "texture" },
    { name: "particle_01", filename: "particle_01.png", extension: "png", type: "texture" },

    // environment
    { name: environmentModel, filename: environmentModel+".glb", extension: "glb", instantiate: false }, 
];

assetsToLoad.forEach((obj) => {
    let assetTask;
    switch(obj.extension) {
        case "png":
        case "jpg":
        case "jpeg":
        case "gif":
            if(obj.type === "texture"){
                assetTask = this._assetsManager.addTextureTask(obj.name, './textures/' + obj.filename);
            }else if(obj.type === "image"){
                assetTask = this._assetsManager.addImageTask(obj.name, './images/' + obj.filename);
            }
            break;

        case "dds":
            assetTask = this._assetsManager.addCubeTextureTask(obj.name, './images/' + obj.filename);
            break;

        case "hdr":
            assetTask = this._assetsManager.addHDRCubeTextureTask(obj.name, './images/' + obj.filename, 512);
            break;

        case "mp3":
        case "wav":
            assetTask = this._assetsManager.addBinaryFileTask(obj.name, './sounds/' + obj.filename);
            break;

        case "babylon":
        case "gltf":
        case "glb":
        case "obj":
            if(obj.instantiate){
                assetTask = this._assetsManager.addContainerTask(obj.name, "", "", './models/' + obj.filename)
            }else{
                assetTask = this._assetsManager.addMeshTask(obj.name, "", "", './models/' + obj.filename)
            }
            break;

        case "json":
        case "txt":
            assetTask = this._assetsManager.addTextFileTask(obj.name, './data/' + obj.filename);
            break;

        default:
            console.error('Error loading asset "' + obj.name + '". Unrecognized file extension "' + obj.extension + '"');
            break;
    }

    assetTask.onSuccess = (task) => {
        switch(task.constructor) {
            case TextureAssetTask:
            case CubeTextureAssetTask:
            case HDRCubeTextureAssetTask:
                this._loadedAssets[task.name] = task.texture;
                break;
            case ImageAssetTask:
                this._loadedAssets[task.name] = task.url;
                break;
            case BinaryFileAssetTask:
                this._loadedAssets[task.name] = task.data;
                break;
            case ContainerAssetTask:
                this._loadedAssets[task.name] = task.loadedContainer;
                break;
            case MeshAssetTask:
                this._loadedAssets[task.name] = task;
                break;
            case TextFileAssetTask:
                this._loadedAssets[task.name] = task.text;
                break;
            default:
                console.error('Error loading asset "' + task.name + '". Unrecognized AssetManager task type.');
                break;
        }
    };

    assetTask.onError = (task, message, exception) => {
        console.log(message, exception);
    };

});

this._assetsManager.onProgress = (remainingCount, totalCount, lastFinishedTask) => {
    this.showLoadingMessage(lastFinishedTask.name + ": "+remainingCount+"/"+totalCount);
};

this._assetsManager.onFinish = () => {
    console.log('loading complete', this._loadedAssets);
    this.showLoadingMessage("loading complete");
};

await this._assetsManager.loadAsync();

And then I just pass around this._loadedAssets to whatever class needs it.

And just for fun, latest screenshot:

6 Likes