26

(54 replies, posted in General)

Storm87 wrote:

Added a second 3D Fighter gameplay template (3rd person view)

woo, wait like a beat em up or tekken force mode were you can run around?

https://snipboard.io/Bb5WMO.jpg

27

(4 replies, posted in General)

You'd want to create two unity ui sprite variables in your own battlegui, one for player 1, the second for player 2. The battlegui should also be inheriting from defaultbattlegui. You'll then place the sprite in the battlegui canvas where you want.

Then in the FixedUpdate or Update methods you'd do a check on the designated player to find out their current meter value. then say if it's greater than or equal to 100% then display the sprite by turning on the spriterenderer, else turn off its spriterenderer to hide it.

There's no out of the box solution for it since the UI system was designed to allow you free code use even if you don't have source.

The system isn't designed with it in mind, no. Sorry.

However, off the top of my head.... you'd create a custom script to the character's prefab that knows where the sprite renderer is being played. If the character is being mirrored in UFE (forgive me because at the moment I forget the exact check but it's something like the character's active controlsScript.mirror. 1 is I believe Player 1 side and -1 is Player 2 side. Assuming that's the case, you could do a check that if the player is being mirrored, then alter the frame in the sprite renderer to the alt. I'd imagine you'd some how make the sprite names formulaic to easily tell when a sprite name is being used, then tell it to switch to the alt. or you can create an array in the same custom script of all the possible adjusted sprites and what to switch them to?

Anyways, that's the best i was able to come up with off the top of my head.

That option will be available in the next version of UFE I believe.

30

(4 replies, posted in General)

You'll have to code one in your BattleGUI code sheet.
In short, when gauge is 100% filled, display button sprite.
When gague is not 100% filled, hide button sprite.

31

(3 replies, posted in General)

Steviebops wrote:

May I ask how you handled hurtboxes for the beam attack?

Not sure if it's how the OP did it, but in games like MVC2/3 they basically have a stream of projectile collision boxes moving full screen or greater in like a row with the boxes slightly overlapping each other for the duration of time of the beam.

32

(5 replies, posted in General)

Depends on how you want running implemented.

There's a thread somewhere that talks about "charge" attacks where you hold a button to charge up something (not charge like Guile in SF). So if you want them to continuously run forward I'd imagine you'd make a move with say a forward, forward input notation that links to a move on the early frames which loops the run animation as long as forward is being held?

33

(25 replies, posted in General)

I haven't researched it at all, so I can't say. I'd imagine you'd need some way to not only load all the resources, but also tell the animation system to use those specific resources and not to load new instances of the resources? That'd be my hypothesis.

34

(25 replies, posted in General)

I think Texture Packer was what I used?

*googles Texture Packer Unity*

Yes, yes it was.

In terms of multiple sprite sheets... not sure. Unless there's a way to load the sheets into memory, be sure it doesn't get garbage collected, and reference that sprite sheet as a cache. Either way I'd expect it to require some sort of custom animation system or code work? Other areas of performance would probably have to be in code performance improvements I presume? I'm only hypothesizing since I have never been successful in completing my own project(s) so I haven't gone through to improve performance specifically - nor do I have experience testing on mobile phones.

35

(18 replies, posted in 2D Gameplay)

Storm87 wrote:

quick question of you dont mind I got 7 errors after copying and pasting that pallet code above.

please help.

https://ibb.co/PwZC6NF

ah, modify the SpriteGhostTrailRenderer.cs, I believe that would be your issue. I did have to modify his code a little where i added these three methods.

        #region Custom Methods
        public SpriteRenderer[] GetRenderers() {
            return _ghostRenderers;
        }

        public Transform GetContainer() {
            return _ghostContainer;
        }

        public int GetIndex() {
            return _ghostIndex;
        }
        #endregion

For Ghosts, I believe I just made what was an existing private int variable public, but this was the end result:

    [Range(1,10)] public int ghosts = 4;

If that doesn't work, then PM me what his default SpriteGhostTrailRenderer.cs file is and I can tell you what needs to be changed.

Since the screens are open source you can just have a button/selection go to a different screen you made rather than a template screen

37

(25 replies, posted in General)

It's up to you, but in general yeah I'd recommend all the sprites for the character to be in one sheet for the character. Projectile attacks like energy beams and such may be best on their own sprite sheets since it would be loading them into memory regardless when the projectile is spawned.

There's a decent program you can find that will take all your individual PNGs of each sprite and compile it into a spritesheet for you with the Unity rectangles set to splice them out easily, it's what I used for Makoto. However, it's been so long I forget what the name of the thing was. If I remember, I'll post it.

38

(25 replies, posted in General)

Just a cursory question as I think we've discussed it before, but your A.I. is using a character prefab with a sprite sheet right? Switching my animations to be on a sprite sheet instead of individual images was a big performance boost for myself when I've tested 2D Sprite system. It'd make sense as well since it's less things to load on the fly into/out of memory.

39

(2 replies, posted in General)

I'd keep a script as a component in the game manager and use events to help you out.

So in the script you'd have an ongoing tracker such as a boolean of specialEnding.

Then when difficulty is selected to hard you set the specialEnding to true else false.
Then when someone presses continue you set the specialEnding to false.

Then add an int so at the OnRoundEnd() event you can see the current health of the winning player. If they have full health then add one to the counter.

Then when you are about to display the ending, add an if check to its call where if specialEnding && perfectCount > requiredNumOfPerfects then play the special ending, else play the normal one.

Yeah, all the GUIs are open and available to you in any package.

Just add it into your custom DefaultBattleGUI script sheet. The sample sheet already has an event handler in there already:

protected override void OnRoundBegin(int roundNumber){
    base.OnRoundBegin(roundNumber);

    if (this.player1GUI != null && this.player1GUI.alert != null && this.player1GUI.alert.text != null){
        this.player1GUI.alert.text.text = string.Empty;
    }
        
    if (this.player2GUI != null && this.player2GUI.alert != null && this.player2GUI.alert.text != null){
        this.player2GUI.alert.text.text = string.Empty;
    }

    if (UFE.gameMode == GameMode.ChallengeMode) {
        this.OnNewAlert(UFE.config.selectedLanguage.challengeBegins, null);

    } else if (roundNumber < UFE.config.roundOptions.totalRounds) {
        this.OnNewAlert(UFE.config.selectedLanguage.round, null);

        if (this.announcer != null && !this.muteAnnouncer){
            if (roundNumber == 1) UFE.PlaySound(this.announcer.round1);
            if (roundNumber == 2) UFE.PlaySound(this.announcer.round2);
            if (roundNumber == 3) UFE.PlaySound(this.announcer.round3);
            if (roundNumber > 3) UFE.PlaySound(this.announcer.otherRounds);
        }
            
    }else{
        this.OnNewAlert(UFE.config.selectedLanguage.finalRound, null);

        if (this.announcer != null && !this.muteAnnouncer){
            UFE.PlaySound(this.announcer.finalRound);
        }

            // If network game, point which character the local player is
            if ((UFE.gameMode == GameMode.NetworkGame || UFE.config.debugOptions.emulateNetwork)
                && networkPlayerPointer != null) {
                    int localPlayer = 1;
                    if (UFE.isConnected) localPlayer = UFE.localPlayerController.player;

                    GameObject pointer = new GameObject("Local Pointer");
                    pointer.transform.SetParent(UFE.GetControlsScript(localPlayer).transform);
                    pointer.transform.localPosition = new Vector3(0, 7, 0);
                    SpriteRenderer spriteRenderer = pointer.AddComponent<SpriteRenderer>();
                    spriteRenderer.sprite = networkPlayerPointer;
                    Destroy(pointer, pointerTimer);
            }
    }

        // If network game, point which character the local player is
        if ((UFE.gameMode == GameMode.NetworkGame || UFE.config.debugOptions.emulateNetwork)
            && networkPlayerPointer != null) {
            int localPlayer = 1;
            if (UFE.isConnected) localPlayer = UFE.localPlayerController.player;

            GameObject pointer = new GameObject("Local Pointer");
            pointer.transform.SetParent(UFE.GetControlsScript(localPlayer).transform);
            pointer.transform.localPosition = new Vector3(0, 7, 0);
            SpriteRenderer spriteRenderer = pointer.AddComponent<SpriteRenderer>();
            spriteRenderer.sprite = networkPlayerPointer;
            Destroy(pointer, pointerTimer);
        }
}

So somewhere in there, probably at the start after

base.OnRoundBegin(roundNumber);

you'll add

    player1.controlsScript.currentGaugePoints[0] = UFE.config.player1Character.maxGaugePoints;
    player2.controlsScript.currentGaugePoints[0] = UFE.config.player2Character.maxGaugePoints;

You should be able to do it via code I'd think on your custom BattleGUI code sheet using OnRoundBegins() event handler?

43

(18 replies, posted in 2D Gameplay)

Yeah, I just guess I'm not understanding fully why you wouldn't be able to swap colors out regardless of the max number of colors you have.

44

(18 replies, posted in 2D Gameplay)

When you say "require 256 colors" you mean if you were to make a palette sheet it'd be 256x1 image, and that all 256 variant colors are being used through the entire sheet?

45

(18 replies, posted in 2D Gameplay)

I had the full sprite sheet and i took each color and made a pixel list.

Specifically. I'd take the sprite sheet, put a new layer up, set Magic Wand tool to work on all layers and have 0 threshold, so it only selects colors of that same exact color. Then I'd color pick that color and mark it down in the corner of the sprite sheet. On the new layer I'd Paint Bucket Fill non-contiguously in the selected areas a solid predefined "green screen" color like rgb(255,0,255) or rgb(0,255,0) whichever worked best for the artwork as contrast. THen move on until I had selected every color variant pixel. By the time the whole sprite sheet looked silhouetted i'd have the full row of pixel variants.

46

(18 replies, posted in 2D Gameplay)

And here's all the code for my AlternatePalettes.cs script which you can see in action here:

https://streamable.com/cw4qzk

using ActionCode2D.Renderers;
using System;
using System.Collections.Generic;
using System.Linq;
using UFE3D;
using UnityEngine;

public class AlternatePalettes : MonoBehaviour
{
    public List<Texture2D> palettes = new List<Texture2D>();
    public List<SpriteRenderer> exGhosts;
    public List<SpriteRenderer> ghosts;
    public List<MoveInfo> exMoves;
    public List<MoveInfo> superMoves;

    public int currentPalette;
    public int startPalette;
    public int superPalette;

    public Texture2D exPalette;
    public Texture2D grayscalePalette;

    private ControlsScript cScript;
    private SpriteRenderer spriteRenderer;
    private SpriteGhostTrailRenderer ghostRenderer;

    private Color exColor = new Color(1f, 0.868932f, 0);
    private Color superColor = new Color(0.45f, 0.53f, 1);

    void Start() {
        cScript = gameObject.GetComponentInParent<ControlsScript>();
        spriteRenderer = gameObject.GetComponent<SpriteRenderer>();
        ghostRenderer = gameObject.GetComponent<SpriteGhostTrailRenderer>();

        ghostRenderer.GetContainer().name = $"Player {cScript.playerNum} Ghosts";
        ghosts = ghostRenderer.GetRenderers().ToList();

        foreach(SpriteRenderer ghost in ghosts) {
            ghost.enabled = false;
            Renderer[] charRenders = ghost.GetComponents<Renderer>();
            foreach (Renderer charRender in charRenders) {
                charRender.material.SetTexture("_PaletteTex", palettes[0]);
                charRender.material.SetTexture("_SwapTex", grayscalePalette);
            }
        }

        //TODO: Add code to set palette of ghosts to an all white ghost for better coloring.
        
        currentPalette = startPalette;
        SetPalette(palettes[currentPalette]);
        if (palettes.Count > 1) {
            if (cScript.playerNum == 2 && UFE.p1ControlsScript.character.name == UFE.GetPlayer2ControlsScript().character.name) {
                AlternatePalettes p1Palette = UFE.p1ControlsScript.gameObject.GetComponentsInChildren<AlternatePalettes>()[0];
                if (p1Palette.currentPalette == currentPalette) {
                    currentPalette = p1Palette.currentPalette + 1;
                    if (currentPalette == palettes.Count) {
                        currentPalette = 0;
                    }
                    startPalette = currentPalette;
                    SetPalette(palettes[currentPalette]);
                }
            }
        }
    }

    private void Update() {
        if (UFE.p1ControlsScript != null && UFE.p2ControlsScript != null && cScript.currentMove != null) {
            CheckIfMoveIsEx();
            CheckIfMoveIsSuper();
        } else if (startPalette != currentPalette) {
            currentPalette = startPalette;
            SetPalette(palettes[currentPalette]);
        } else {
            HideAllGhosts();
        }
    }

    public void HideAllGhosts() {
        foreach (SpriteRenderer ghost in ghosts) {
            ghost.enabled = false;
            ghost.sortingOrder = -1;
        }
    }

    public void CheckIfMoveIsEx() {
        foreach (MoveInfo move in exMoves) {
            if (cScript.currentMove.moveName == move.moveName) {
                EnableExGhosting(ghostRenderer.GetIndex());
                break;
            }
        }
    }

    public void CheckIfMoveIsSuper() {
        foreach (MoveInfo move in superMoves) {
            if (cScript.currentMove.moveName == move.moveName) {
                EnableSuperGhosting();
                break;
            }
        }
    }

    public void EnableSuperGhosting() {
        for(int i = 0; i < ghosts.Count; i++) {
            ghosts[i].color = superColor;
            ghosts[(ghostRenderer.GetIndex() + i) % ghosts.Count].sortingOrder = spriteRenderer.sortingOrder - (i + 1);
            if (cScript.currentMove.currentFrame >= ghosts.Count) {
                ghosts[i].enabled = true;
            } else if (cScript.currentMove.currentFrame >= 0) {
                ghosts[ghostRenderer.GetIndex() % ghosts.Count].enabled = true;
            }
        }

        // TODO: There's some sort of super palette.. have to figure out and create that one too.
    }

    public void EnableExGhosting(int index) {
        HideAllGhosts();

        // Render 3rd and 6th oldest ghosts on the same frame every 6th frame of the move
        int third = (index + 5) % ghostRenderer.ghosts;
        int sixth = (index + 9) % ghostRenderer.ghosts;
        ghosts[third].color = exColor;
        ghosts[sixth].color = ghosts[third].color;
        ghosts[third].enabled = cScript.currentMove.currentFrame % 2 == 0;
        ghosts[sixth].enabled = ghosts[third].enabled;
        ghosts[third].sortingOrder = spriteRenderer.sortingOrder - 1;
        ghosts[sixth].sortingOrder = ghosts[third].sortingOrder - 1;

        // Change to pale palette every 3rd frame after the 6th frame.
        if (UFE.p1ControlsScript.currentMove.currentFrame > 3 && (UFE.p1ControlsScript.currentMove.currentFrame + 2) % 3 == 0) {
            currentPalette = -1;
            SetPalette(exPalette);
        } else if (startPalette != currentPalette) {
            currentPalette = startPalette;
            SetPalette(palettes[currentPalette]);
        }
    }

    public void SetPalette(Texture2D texture) {
        Renderer[] charRenders = gameObject.GetComponents<Renderer>();
        foreach (Renderer charRender in charRenders) {
            charRender.material.SetTexture("_SwapTex", texture);
        }
    }
}

47

(18 replies, posted in 2D Gameplay)

Here you can see the custom scripts I added to the Makoto prefab. With these I was able to code using the event system advanced code methods listed in the Wiki to determine if specific palettes should be switched up for each player, including code that uses the ghost trail add-on to create the EX move and Super effect Third Strike offers.

https://snipboard.io/iPHLDl.jpg
https://snipboard.io/wN7HEa.jpg
https://snipboard.io/e9DUtp.jpg

48

(18 replies, posted in 2D Gameplay)

Hey Storm,

From what I remember when I worked on it, I was having issues with using the add-on's "Just tell me what colors to look out for" method, and just found it easiest to do the single row of all the colors in each column. Sadly it didn't seem to support multi-row palettes as it would have made it easier I'm sure. In the above example from Third Strike, Makoto has 79 unique colors so my color palette file was 79x1 in size. Where the entire sprite sheet was 4096x3562 in size.

In that case I used a separate 79x1 png file to control the palette coloring for the suggested plugin instead of using the sprite 4096 spritesheet itself.

Also, no, I haven't looked at the asset you linked, but I expect you might be able to use it just as well. I was just sharing how I got mine to work without having to modify the UFE code at all.

49

(18 replies, posted in 2D Gameplay)

The shader I used didn't seem to have any color limitations on it. What are you having a hard time with?

50

(2 replies, posted in Showcase)

Very stylish