Top Top
code.PNG

Code Snippets by Jarory

*The site has been experiencing some issues with the embedded Gists. Please refresh the page if they do not show. 


Concrete Rose Callback System

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Card : CardBase
{
public GenreType.MusicGenre genre;
public ElementType.elements primaryElement;
public ElementType.elements subElement;
public CardActionType.actionType actionType;
public UsageType.Usage usageType;
public UsageType.UsageClass usageClass;
public ExpenseData expense;
public StatusEffectData statusEffectType;
public int strength;
public int force;
public int range;
public string description;
}
view raw Card.cs hosted with ❤ by GitHub
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
public class ToggleReflector : MonoBehaviour
{
public bool active = false;
public float timeActive = 0.5f;
public TurntableController activeRecord;
public int damageMultiplier = 1;
public float reflectSpeed = 10;
public ElementType.elements element = ElementType.elements.Earth;
[SerializeField]
private bool ignoreSpriteRenderer = true;
[SerializeField]
private Collider2D objectCollider;
[SerializeField]
private SpriteRenderer spriteRenderer;
private ParticleSystem reflectorPFX; // should this be serialized?
private void Start()
{
if (!objectCollider)
{
objectCollider = gameObject.GetComponent<Collider2D>();
}
if (!spriteRenderer)
{
spriteRenderer = gameObject.GetComponent<SpriteRenderer>();
}
reflectorPFX = gameObject.GetComponentInChildren<ParticleSystem>();
reflectorPFX.enableEmission = false;
}
public void ToggleReflectorOn(Collider2D playerCollider, int timeMultiplier, TurntableController turntable)
{
active = true;
activeRecord = turntable;
damageMultiplier = turntable.GetMultiplier();
element = turntable.attackData.primaryElement;
objectCollider.enabled = true;
spriteRenderer.enabled = ignoreSpriteRenderer ? false : true;
//reflectorPFX.enableEmission = true;
SetTimer(playerCollider, timeMultiplier);
}
public virtual async Task SetTimer(Collider2D playerCollider, int timeMultiplier)
{
await TimeSpan.FromSeconds(DetermineTimeActive(timeMultiplier));
ToggleOffImeediate();
}
public void ToggleOffImeediate()
{
objectCollider.enabled = false;
spriteRenderer.enabled = false;
reflectorPFX.enableEmission = false;
active = false;
}
protected virtual float DetermineTimeActive(int timeMultiplier)
{
return timeActive * timeMultiplier;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TurnTableActionManager : MonoBehaviour
{
public static TurnTableActionManager instance;
public Collider2D playerCollider;
public CrossfadeController crossFader;
public ToggleReflector actionReflector;
private AudioSource scratchSound;
private void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Destroy(this.gameObject);
}
}
private void Start()
{
if (scratchSound == null)
{
scratchSound = gameObject.GetComponent<AudioSource>();
}
}
/// <summary>
/// Logic to determine which type of action to take on turntable press
/// </summary>
/// <param name="turntable"></param>
public void TriggerTurntableAction(TurntableController turntable)
{
Card card = turntable.attackData;
var actionType = card.usageClass;
switch (actionType)
{
case UsageType.UsageClass.Action:
OnActionTypeFound(turntable);
Debug.Log("Action type called");
break;
case UsageType.UsageClass.Support:
Debug.Log("Support type called");
//TODO IMPLEMENT
break;
case UsageType.UsageClass.Movement:
Debug.Log("Movment type called");
//TODO IMPLEMENT
break;
default:
break;
}
}
public void OnActionTypeFound(TurntableController turntable)
{
actionReflector.ToggleReflectorOn(playerCollider, crossFader.DetermineMultiplier(turntable), turntable);
scratchSound.Play();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TurntableController : MonoBehaviour
{
public Card attackData;
public bool isLeft = false;
public CrossfadeController crossfader;
[SerializeField]
private int multiplier = 1;
[SerializeField]
private CombatCharacterController player;
private ActionTimer actionBar;
// Start is called before the first frame update
void Start()
{
if (player == null)
{
player = PlayerInfoHolder.instance.gameObject.GetComponent<CombatCharacterController>();
}
if (player != null)
{
player = crossfader.player;
if (isLeft)
{
player.onLeftTurntableButtonPressed.AddListener(delegate { OnTurntableActivate(); });
}
else
{
player.onRightTurntableButtonPressed.AddListener(delegate { OnTurntableActivate(); });
}
actionBar = gameObject.GetComponentInChildren<ActionTimer>();
}
else
{
Debug.LogError("Player Is null !!!!");
}
}
public void OnTurntableActivate()
{
int multiplier = crossfader.DetermineMultiplier(this);
//Only act if the actionbar is full and the crossfader is not on the opposite side
if (actionBar.CanAct && multiplier > 0)
{
TurnTableActionManager.instance.TriggerTurntableAction(this);
actionBar.ResetActionBar();
}
}
public int GetMultiplier()
{
return multiplier * crossfader.DetermineMultiplier(this);
}
}

For Concrete Rose, we needed a flexible system that can support multiple types of actions on a single button press, depending on JSON data feed into the combat scene. This set up allows us to populate card data from JSON (Not shown) and then use that data in a series of callbacks attached to singleton managers. Classes can subscribe to the managers’ functions without having to get an instance of them stored in memory. In this example, the TurnTableActionManager stores a reference to a reflector. When either turn table action wants to activate that reflector, they ask the manager to do so by providing their own data from their card. This in turn allows the reflector to know what properties, such as elements and strength, it needs to activate with.


Fenced AI State Example

private void OnPlayerNoticed(GameObject target)
{
player = target;
//See editor for methods to invoke. Should be "TransitionTo()"
PlayerNoticed.Invoke();
}
view raw AIObserver.cs hosted with ❤ by GitHub
public Dictionary<AIStates, IAIState> stateNameDictionary = new Dictionary<AIStates, IAIState>();
void Update()
{
stateNameDictionary[state].During();
}
//Outside the class
public enum AIStates
{
Pursuing,
Patrolling,
Investigating,
Idle
}
public void During()
{
// Choose the next destination point when the agent gets
// close to the current one.
if (!movment.agent.pathPending && movment.agent.remainingDistance < 0.5f)
{
GotoNextPoint();
}
//if we ever stop moving turn this flag
if (movment.agent.isStopped)
{
movment.Message("OnStoppedMoving");
}
}
void GotoNextPoint()
{
// Returns if no points have been set up
if (points.Length == 0)
return;
// Set the agent to go to the currently selected destination.
movment.MoveTo(points[destPoint]);
// Choose the next point in the array as the destination,
// cycling to the start if necessary.
destPoint = (destPoint + 1) % points.Length;
}
public void TransitionTo()
{
AIStateManager.instance.state = AIStates.Patrolling;
movment.ResetSpeed();
}
view raw PatrolState.cs hosted with ❤ by GitHub
public void SetPursuit(Transform newTarget = null)
{
if (newTarget)
{
target = newTarget;
}
else
{
target = observer.player.transform;
}
}
public void During()
{
movment.MoveTo(target.position);
}
public void TransitionTo()
{
AIStateManager.instance.state = AIStates.Patrolling;
movment.ResetSpeed();
}
view raw PursueState.cs hosted with ❤ by GitHub

I needed a dynamic AI system that could detect the player and make important decisions. Using a state machine, Unity's event system, and some basic pathfinding, I was able to create a set of scripts that allows the AI state manager to call a During() function that varies by state. Each state also has a TransitionTo() function which tells the state manager to enter this new state, dropping the other one. 


MasterHero Power Up Interface

public interface IPowers
{
void Activation(GameObject target);
}
view raw IPowers.cs hosted with ❤ by GitHub
public void Activation(GameObject target)
{
Debug.Log("Phase");
Vector3 rayVector = new Vector3(target.transform.position.x,
target.transform.position.y, 0.0f);
RaycastHit hit;
if (Physics.Raycast(rayVector, target.transform.right, out hit, 10.0f))
{
Debug.Log(hit.transform.gameObject.name);
Vector3 phasePosition = new Vector3(hit.transform.position.x + target.GetComponent<Renderer>().bounds.size.x,
target.transform.position.y, 0.0f);
if (target.transform.gameObject.name.Contains("Ai"))
{
target.transform.position = new Vector3(phasePosition.x + target.GetComponent<SphereCollider>().radius, phasePosition.y, phasePosition.z);
target.GetComponent<AiController>().isFighting = false;
}
else
{
target.transform.position = phasePosition;
}
}
else
{
Debug.Log("we did not phase");
}
}
view raw Phase.cs hosted with ❤ by GitHub
if (Input.GetKeyDown(KeyCode.Alpha2))
{
if (Slot2.GetComponentInChildren<DragHandler>() != null)
{
powers[Slot2.GetComponentInChildren<DragHandler>().Power].Activation(gameObject);
timeToNextActivation = Time.time + activationDelay;
}
}

This code was used in the game MasterHero to create a dynamic power up system. Since players could drag and drop their powers around in various slots mapped to different number keys, I had to have each number key use powers on key down agnostic of the power in the slot. I used an interface to accomplish this. 


Power-Up Interface in C++

void ABasePlayerController::StopUsingPowerUp()
{
if (PowerUp)
{
UE_LOG(LogTemp, Warning, TEXT("%s"), *PowerUp->GetName());
//Does that blueprint's base class implment the interface?
bool ImplementsInterface = PowerUp->GetClass()->ImplementsInterface(UIPowerUp::StaticClass());
//If Powerup implements the interface, cast it to an interface
if (ImplementsInterface)
{
TScriptInterface<IIPowerUp> PowerUpInterface;
PowerUpInterface.SetInterface(Cast<IIPowerUp>(PowerUp));
PowerUpInterface.SetObject(PowerUp);
if (PowerUpInterface != nullptr)
{
PowerUpInterface->Execute_StopUsing(PowerUp, CurrentControlledPawn);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Isnull"));
}
}
}
}
UINTERFACE(BlueprintType)
class MIRRORIMAGE_API UIPowerUp : public UInterface
{
GENERATED_UINTERFACE_BODY()
};
/**
*
*/
class MIRRORIMAGE_API IIPowerUp
{
GENERATED_IINTERFACE_BODY()
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
//classes using this interface must implement UsePowerUp
//Called when the "B" button is pressed
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Powerup")
bool Use(ACharacter* Scamp);
//Called on release of the b button, not everyone needs to implement it
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Powerup")
bool StopUsing(ACharacter* Scamp);
//Called when the player hits the ground
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Powerup")
bool Refresh();
//Called when the power up is assigned to scamp.
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Powerup")
EProjectileType Assign(APlayerCharacter * Scamp);
};
view raw IPower.h hosted with ❤ by GitHub
#include "IPowerUp.h"
#include "MirrorImage.h"
//Constructor script
UIPowerUp::UIPowerUp(const class FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
view raw IPowerup.cpp hosted with ❤ by GitHub

I wanted to draw off my interface experience with power ups before. C++ does not have support for interfaces, but Unreal does have a solution for that. Here is my implementation of that. I did not write any of the power ups themselves, just the architecture for using them. I may write a power up in the future as this project is on going. 


Beat Detection

void Start ()
{
audio = gameObject.GetComponent<AudioSource>();
bpm = UniBpmAnalyzer.AnalyzeBpm(audio.clip);
calledOnDownBeat = false;
calledOnUpBeat = false;
if (onDownBeat == null)
{
onDownBeat = new UnityEvent();
}
if (onUpBeat == null)
{
onUpBeat = new UnityEvent();
}
//calculate how many seconds is one beat
//we will see the declaration of bpm later
secPerBeat = 60f / bpm;
//record the time when the song starts
dsptimesong = (float)AudioSettings.dspTime;
//start the song
GetComponent<AudioSource>().Play();
}
void Update()
{
//calculate the position in seconds
songPosition = (float)(AudioSettings.dspTime - dsptimesong);
//calculate the position in beats
songPosInBeats = songPosition / secPerBeat;
if (Mathf.Floor(songPosInBeats) % 4 == 0 || Mathf.Floor(songPosInBeats) % 4 == 2)
{
if (!calledOnDownBeat)
{
onOnbeatDetected();
Debug.Log("Down Beat");
onDownBeat.Invoke();
calledOnDownBeat = true;
calledOnUpBeat = false;
}
}
else
{
if (!calledOnUpBeat)
{
onUpBeat.Invoke();
calledOnDownBeat = false;
calledOnUpBeat = true;
}
}
}
view raw SongManager.cs hosted with ❤ by GitHub

For this assignment I had to create a rhythm game. Using a BPM analyzer and some help from Gamasutra I was able to create an algorithm that could allow the player to fire but only on the down beat.


Swap Function

void ABasePlayerController::SwapTransition(float DeltaTime)
{
if (CurrentControlledPawn->SwapTransitionTime <= 1.0f)
{
FHitResult Hit;
FVector PrevPosition = CurrentControlledPawn->GetActorLocation();
bNeedsToFloat = true;
CurrentControlledPawn->DisableInput(this);
CurrentControlledPawn->SetActorLocation(FMath::Lerp(CurrentControlledPawn->SwapStartLocation,
SwapLocation, CurrentControlledPawn->SwapTransitionTime));
// This is for checking if we swapped through an enemy
if (GetWorld()->LineTraceSingleByChannel(Hit, PrevPosition, CurrentControlledPawn->GetActorLocation(), ECC_Visibility))
{
if (Hit.Actor.Get()->GetComponentByClass(UPowerUpComponent::StaticClass()))
{
UPowerUpComponent* NewPowerUp = Cast<UPowerUpComponent>(Hit.Actor.Get()->GetComponentByClass(UPowerUpComponent::StaticClass()));
PowerUp = NewPowerUp->PowerUp;
if (PowerUp)
{
//CurrentControlledPawn->CurrentPowerup = NewPowerUp->PowerUp
//Does that blueprint's base class implment the interface?
bool ImplementsInterface = PowerUp->GetClass()->ImplementsInterface(UIPowerUp::StaticClass());
//If Powerup implements the interface, cast it to an interface
if (ImplementsInterface)
{
TScriptInterface<IIPowerUp> PowerUpInterface;
PowerUpInterface.SetInterface(Cast<IIPowerUp>(PowerUp));
PowerUpInterface.SetObject(PowerUp);
if (PowerUpInterface != nullptr)
{
CurrentControlledPawn->CurrentPowerup = PowerUpInterface->Execute_Assign(PowerUp, CurrentControlledPawn);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Isnull"));
}
}
}
UE_LOG(LogTemp, Warning, TEXT("Setting power up"));
}
// Check if we can kill an enemy
if (CurrentControlledPawn->CanKill() && Hit.Component->ComponentHasTag("Enemy"))
{
CurrentControlledPawn->KillEnemy(Hit.Actor.Get());
}
}
CurrentControlledPawn->SwapTransitionTime += DeltaTime * (CurrentControlledPawn->SwapSpeed / SwapDistance);
}
//Assume we got there within the half second, reset everything we need to.
else
{
if (IsValid(CameraActor))
{
CameraActor->ExitSwapLag();
}
CurrentControlledPawn->SetActorEnableCollision(true);
CurrentControlledPawn->EnableInput(this);
CurrentControlledPawn->bMoving = false;
CurrentControlledPawn->SetActorLocation(SwapLocation);
CurrentControlledPawn->SwapTransitionTime = 0;
CurrentControlledPawn->GetCharacterMovement()->Velocity = FVector::ZeroVector;
//halt code execution for a fraction of a second to allow the model to know if it's falling or not
GetWorldTimerManager().SetTimer(
PauseInTransitionHanlder, this, &ABasePlayerController::ResetGravity, DeltaTime * 2, false);
}
}

For Project Imp our core mechanic revolves around swapping positions with clones of yourself that you can spawn around the world. Swapping positions with an enemy in the middle allows you to steal some of their power. This is the base of that system that I designed.