Sonic 1 - Best Way to Add Multiple Characters

Discussion in 'Discussion and Q&A Archive' started by ProjectFM, Jun 28, 2015.

  1. ProjectFM

    ProjectFM Optimistic and self-dependent Member

    Joined:
    Oct 4, 2014
    Messages:
    912
    Location:
    Orono, Maine
    What is the best (as in works efficiently or easiest to implement) way to add extra characters with different art, mappings, DPLC's, and moves to Sonic 1? Here are some ways it could be added:

    1. Creating a different object for each character.

    2. Using checks and tables to load different character stuff depending on the character

    3. Using checks to branch to different pieces of code depending on the character (for example, a different Sonic_Animate or Obj01MdNormal per character.)

    I don't think it matters but just in case you need to know, I'm using the Sonic 128 Hivebrain disassembly.

    I'll be at camp for 2 weeks so don't expect an answer from me until July 12.

    Edit: Sorry for creating so many topics. It said that it failed to post the topic so I tried several times to get it to work.
     
    Last edited by a moderator: Jun 28, 2015
  2. AURORA☆FIELDS

    AURORA☆FIELDS so uh yes Exiled

    Joined:
    Oct 7, 2011
    Messages:
    759
    well in terms of saving space you most likely want to use all the code within same object, and use tables for things different for all characters (for example moves) and check for things only for a single character (like Tails having different height to Sonic and Knuckles). Generally copying all the code for different character like done in Sonic 2 is wasteful for no major improvements (in terms of speed few cycles spent vs few kilobytes of code the former is usually better). Generally the ways implemented in my multiple character guide is fairly compact and efficient as it is, and personally I think its good for reference.
     
  3. MarkeyJester

    MarkeyJester ♡ ! Member

    Joined:
    Jun 27, 2009
    Messages:
    2,867
    Easy - Using the same object, and gradually adding conditional junction flags for alternate moves, art, animations, etc.  This will save you beginning time, as you'll see results immediately, and are therefore less inclined to procrastinate, then give up.

    Efficiently - Having a seperate object for the character, and reusing only the subroutines that belong to physics or functions, that will remain the same between characters, whilst the differences will be given their own unique subroutines (even if it's a copy, paste and alter.  The lack of junction flags will save time).  This will set you up for the future, making you prepared to move the game to something more complicated, which takes more processing time.  You will be able to have both characters running side by side with the fewest cycles used possible, and alterations can be made to a specific character without requiring to put junction flags in correctly, or risk damaging another character by mistake without realising it.
     
  4. ThomasThePencil

    ThomasThePencil resident psycho Member

    Joined:
    Jan 29, 2013
    Messages:
    910
    Location:
    the united states. where else?
    Well, in Sonic 2, this can be accomplished for the main character by using Player_Mode to determine who's supposed to be on screen. Here's the example in my hack:

    ; ===========================================================================
    ; ---------------------------------------------------------------------------
    ; Player logic object. Branches to different character logic code chunks
    ; based on the value in Player_mode.
    ; ---------------------------------------------------------------------------
    Obj01:
    moveq #0,d0
    move.w (Player_mode).w,d0
    subq.w #1,d0
    add.w d0,d0
    move.w PlayerLogicRoutines(pc,d0.w),d1
    jmp PlayerLogicRoutines(pc,d1.w)
    ; ===========================================================================
    PlayerLogicRoutines:
    dc.w Obj01_Sonic - PlayerLogicRoutines ; 0 ; Sonic The Hedgehog
    dc.w Obj01_Tails - PlayerLogicRoutines ; 1 ; Miles "Tails" Prower
    dc.w Obj01_Knuckles - PlayerLogicRoutines ; 2 ; Knuckles The Echidna
    dc.w Obj01_Sonic - PlayerLogicRoutines ; 3 ; Shadow The Hedgehog
    (not done)
    dc.w Obj01_Derpy - PlayerLogicRoutines ; 4 ; Derpy Hooves (unlockable)
    ; ===========================================================================
    include "Character Code/Sonic.asm"
    ; ===========================================================================
    include "Character Code/Tails.asm"
    ; ===========================================================================
    include "Character Code/Tails' Tails.asm"
    ; ===========================================================================
    include "Character Code/Knuckles.asm"
    ; ===========================================================================
    include "Character Code/Derpy.asm"
    ; ===========================================================================While I'm not entirely sure of how Sonic 1 works differently from Sonic 2 due to only working with it for 1 miniature hack and never touching it again, I'm pretty sure something similar could be implemented with little trouble. This method also allows for great versatility, as you can edit one character without worrying about fucking up the rest.
    As for the best way to add a new character's code? If you're using the method above, just copy Sonic's code, change some subroutine labels in the new copy so that they don't conflict the old copy, and work from there.
    And the method of actually letting the player play as your new character...well, I won't give you much instruction there, as there are many different methods. Just find one that you'd prefer to use in your hack, and enjoy your newly added character.

    ...oh, and you might want to give the new character a new move or something to clearly distinguish their gameplay from that of Sonic =P
     
  5. DanielHall

    DanielHall Well-Known Member Member

    Joined:
    Jan 18, 2010
    Messages:
    860
    Location:
    North Wales
    @ThomasSpeedRunner:

    Nice! A problem with that is that those word tables are PC-relative, and can cause problems should any of the code go past $7FFF/-$7FFF in size (which I'd imagine would happen if whole character data is being included). You could change that by making the routine handle longword tables instead of word tables (or just adding a jmpto-esque routine should that happen. =P).
     
    Last edited by a moderator: Jun 29, 2015
  6. vladikcomper

    vladikcomper Well-Known Member Member

    Joined:
    Dec 2, 2009
    Messages:
    419
    ThomasSpeedrunner, you're just complicating things with your method. You're merging several objects into one, with a code to switch between them, virtually implementing yet another object loader/executor over the standard one. So technically speaking, it's the same as using separate objects, expect for:

    1) It looses 52 cycles for that switching code (I counted)

    2) You're complicating program flow by implementing yet another object loader (although, a primitive one)

    3) You're loosing some flexibility in terms of object manipulation, as...

    4) Your implementation seemingly makes it impossible to load sidekick in Sonic 2. Even if that was totally unneeded for your own hack, the fact the flexibility has got worse with your solution isn't a good thing.

    I know, sometimes one may sacrifice flexibility for the sake of performance or simplicity. But your solution is neither of this: it lowers the performance a bit and trust me, it only complicates your game's architecture.
     
  7. ProjectFM

    ProjectFM Optimistic and self-dependent Member

    Joined:
    Oct 4, 2014
    Messages:
    912
    Location:
    Orono, Maine
    Sorry to bump this topic but I have succeeded in adding another character in my hack (Original Sonic 128 HiveBrain Disassembly) by creating a separate object. Unfortunately, I have one problem: When the second character is being used, having the in-air flag set (2,$22(a0) and 6,$22(a0)) causes it to go to position $00000000 of the level. I've looked through piece of code with btst #2,$22(a0) and btst #6,$22(a0) and have been unable to find the cause of the problem. Does anyone have an idea of what could be making this happen?
     
    Last edited by a moderator: Jul 30, 2015
  8. Niko

    Niko All's well that ends well, right? Member

    Joined:
    Mar 1, 2014
    Messages:
    245
    Location:
    $C800
    @Project1114, did you make sure that the variable at (a0) is performing on the variable you want?
    It seems like it would be changing the x and y position for the player, so I would recommend making sure both players have proper player-related variables, so that (a0) can affect the player right, rather than affect it's variable of the location.

    Of course, I might just be completely wrong.
    I'm still learning, but I'd like to help. 
     
  9. ProjectFM

    ProjectFM Optimistic and self-dependent Member

    Joined:
    Oct 4, 2014
    Messages:
    912
    Location:
    Orono, Maine
    I made it load object 2 instead of object 1 by moving 2 to $FFFFD000 making object 2 work exactly the same as Sonic-except for whatever is causing the problem.
     
  10. Niko

    Niko All's well that ends well, right? Member

    Joined:
    Mar 1, 2014
    Messages:
    245
    Location:
    $C800
    And, you do have the air checking for object 2 set to affect object 2, right?
     
  11. ProjectFM

    ProjectFM Optimistic and self-dependent Member

    Joined:
    Oct 4, 2014
    Messages:
    912
    Location:
    Orono, Maine
    What air? Moving object 2 to $FFFFD000 makes everything that checks for object 1 check for object 2 instead.
     
  12. Niko

    Niko All's well that ends well, right? Member

    Joined:
    Mar 1, 2014
    Messages:
    245
    Location:
    $C800
    You said "having the in-air flag set" is what's causing a problem, have I misread?
    As long as both Sonics have their own VRAM, and both VRAM addresses are free and set up for Sonic-action, that would work just fine.
    The in-air check might be accidentally messing with VRAM that isn't in that address, so it steps back to the x/y position stored. 

    I would recommend setting aside VRAM for the air-checking if you need this variable. 

    "When the second character is being used, having the in-air flag set (2,$22(a0) and 6,$22(a0)) causes it to go to position $00000000 of the level," so making the in-air flag affect the in-air flag would be the solution. You can also try commenting out the problematic codes to see what does and doesn't happen. 
     
  13. EMK-20218

    EMK-20218 The Fuss Maker Exiled

    Joined:
    Aug 8, 2008
    Messages:
    1,067
    Location:
    Jardim Capelinha, São Paulo
    ​This is actually the best method, I think. This makes me remember that this was the method Tornado has applied to add Tails and Knuckles as additional characters in my hack. This is the hardest one, though this one gives more confidence that the result will be exactly like we want. I prefer the harder but efficient work, than the easier method which may not give the wanted results in the end.
     
  14. ProjectFM

    ProjectFM Optimistic and self-dependent Member

    Joined:
    Oct 4, 2014
    Messages:
    912
    Location:
    Orono, Maine
    ​Do you think I'm having my second character like Tails in Sonic 2/3 whose alongside Sonic? That's not what I'm doing. The point of having object 2 is to make it so the game can easily load the the second character's art and moves instead of using object 1 with a bunch of checks. I have no plans for having 2 characters onscreen at once.