coderman64 / motobug-engine

A Sonic the Hedgehog-style game engine for the open web
https://coderman64.github.io/motobug-engine
19 stars 1 forks source link

add customizable characters #20

Open coderman64 opened 2 years ago

coderman64 commented 2 years ago

While Motobug comes with 3 characters as of now, people probably want to add other characters. I think instead of adding the characters one by one to the engine code itself, it makes sense to make an easier system for adding characters instead. I think it should be based on the items system, where each character is an object, and things like double jump abilities are governed by specific functions in that object. The project already stores character stats and things as an object, but it needs some effort to make double jump abilities fit into a standardized function format.

pink-bot commented 2 years ago

What functions should be handled inside the character object/class and what should remain in the main game loop? I'm thinking the controls function could be moved there and accept inputs as parameters, but I also see a separate controlpressed function that contains every doublejump ability and seems to only be called if you're using touch/gamepad controls. I've also noticed code in the motobug that checks to see if silver's levitation ability is being used. Could this be moved into the character class somehow?

Also, should each character be its own class derived from the character class or should all of the characters attributes (including abilities) be defined by the parameters? I think there's a way to execute a string of text as code but it could look pretty messy.

coderman64 commented 2 years ago

A major part of this issue is designing how this functionality works. So I don't know yet. However, I think having a default character class that is inherited by other classes would work well.

Currently, there are a few js files that are auto-generated from mb studio, or are supposed to be user-editable including user-items and user-backgrounds.

As for "executing a string of text as code", currently, levels are loaded in by adding js source files at runtime, which I imagine could be done with characters. However, I don't think it would be necessary, as most characters could be loaded in at program start (like with items and backgrounds).

I'll probably try to suggest a more specific format at some point.

coderman64 commented 2 years ago

Here's some of my thinking on the subject:

how characters currently look:

var sonic = {
    // sonic's sprite sheet
    spriteSheet: newImage("SonicSheet2.png"),
    // sonic's animation data
    anim: {
        stand: grabAnim(0, 0, 40, 40, 1),
        jog: grabAnim(0, 40, 40, 40, 8),
        run: grabAnim(0, 80, 40, 40, 4),
        jump: grabAnim(0, 316, 31, 31, 8),
        dropDash: grabAnim(31, 316, 31, 31, 1),
        skid: grabAnim(40, 203, 40, 40, 1),
        death: grabAnim(80, 203, 40, 40, 1),
        crouch: grabAnim(40, 243, 40, 40, 1),
        spindash: grabAnim(0, 284, 32, 32, 5),
        push: grabAnim(0, 164, 34, 39, 4),
        hurt: grabAnim(0, 203, 40, 40, 1),
        sprung: grabAnim(0, 120, 43, 43, 5),
    },
    // Sonic's default character data
    char: {
        x: 128 * 34,
        y: 128 * 1 + 40,
        startX: 128,
        startY: 168,
        Gv: 0.001,//ground velocity. The sign is used for direction
        xv: 0,//x-velocity
        yv: 0,//y-velocity
        grounded: false,
        frameIndex: 0,
        currentAnim: anim.jump,
        animSpeed: 1,
        rolling: false,
        angle: 0,
        state: -1, // -1=air state; 0=ground state; 1=right wall state; 2=ceiling state; 3=left wall state;
        golock: 0, //forces player to go in a certain direction for a certain amount of time
        goingLeft: false,
        spindashCharge: 0,
        jumpState: 0,// 0 = fell/walked off a cliff; 1 = jump; 2 = hurt; 3 = spring;
        hurt: false,
        rings: 0,
        dropDash: true,
        pDropDash: false,
        dropCharge: 0,
        layer: 0,
        pAngle: 0,
        invincible: 0,
        deathTimer:0,
        deathFade:0,

        //constants (only change if you know what you are doing)
        ACC: 0.046875,  // acceleration
        DEC: 0.5,       // deceleration
        FRC: 0.046875,  // friction
        TOP: 6,         // top speed (pixels/frame)
        JMP: 6.5,       // jump speed
        GRV: 0.21875,   // gravity
    }
};

how the items look (for reference):

var spring = function(x,y,w,h,src,power,angle){
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.img = globalImgCache.getImg(src);
    this.power = power;
    this.canvi = document.createElement("canvas");
    this.canvi.width = this.w;
    this.canvi.height = this.h;
    this.c = this.canvi.getContext("2d");
    this.frame = 0;
    this.destroy = false;
    this.targetable = true;
    this.hrid = "spring";
    this.hit = function(char){
       // ...
    }
    this.draw = function(ctx,camx,camy){
       // ...
    }
}

so, with spring, you can easily define what it looks like and what it does in a single function, for the most part.

with the current character, it has some constants (which should be editable). some runtime variables (which shouldn't be editable), and double-jump abilities are determined by flags, which aren't sustainable on a larger scale. Additionally, you should be able to customize the anim object, and the sprite sheet. Anim and sprite sheet could be auto-generated by MBStudio, but actual code should probably be edited directly.

So, like the items, you should have some standard function names (like hit and draw in this instance), which, if they exist, are run in these scenarios, but can be omitted. I'm thinking things like on_doublejump on_doublejump_hold on_doublejump_release. Additional class variables can be added as well (example: power in the spring script is non-standard, and is only for this item type). This can be used to manage things like timers or limiting it to one double-jump.

pink-bot commented 2 years ago

The big sticking point for me right now is moving the double jump code into the main control function. It's currently in the control pressed and control released functions which aren't even called inside the main control function and I was wondering if you could give me a better idea of why it was designed that way before I start breaking stuff.

I do know one of the things they are used for is touch/gamepad controls, I can emulate touch controls in a browser but gamepads im not sure about. I still dont get why theyre only used for the jump key though.

edit: in theory I could detect if the jump button was released during a jump without creating special event listeners, just by checking if the jump key is up while sonic is airborne in jump state 1, but I again I want to make sure i understand the intent of the above functions before I make that kind of change.

Edit: I finally realized how confusing this post is. Basically I'm trying to consolidate all the code that uses player inputs so it will be easier to avoid undesirable behavior when implementing a class. I'm just not sure how to change the touchscreen/game pad code so it still works but doesn't directly control doublejumps. I think it makes sense to have the input and physic functions inside the character class instead of the main loop so that the player can call a function with inputs as parameters and the the character object can handle all the resulting physical calculations