09 March 2025

Final Team 4 Scaffolding Progress Review

Stanley Freihofer
Luke Whipple 


Description

The "Tank Survivors" controller is an absurd way to play Vampire Survivors that adds a unique challenge to what is normally a very easy game. Visually, it contains parts made out of a mish-mash of famous in-game items, both of which are great for this bizarre style of play and keep the "tank" feel when used.

The first is the wheel (built on a rotary encoder), which has a holy cross shape and a garlic knob. This allows you to change how much you'll turn in a particular direction while moving. The cross exists to fit the general vibe of the game but the garlic is a weapon in game that gives you an aura that damages enemies near you, encouraging you to charge at them.

The second is the lever (built on a slide potentiometer) that has a handle in the shape of the Night Sword from the game, which heavily damages nearby enemies whenever you get hit. This allows you to change how fast you will move. The sword, just like the garlic, encourages players to charge at enemies


Sketches



Schematic








Psuedo Code

#include Mouse

 

float currentTankSpeed = 0f;

int currentTankRotation = 0; // in degrees

 

float maxTankSpeed = 10f;

int maxTankRotation = 15;

 

int mouseLastX;

int mouseLastY;

bool modeJustSwitched = false;

 

const int frameDelta = 52 // Milliseconds between frames.

 

void start() {

    startUpAnimation();

    powerSwitch = pinMode(D7, INPUT_PULL_UP);

 

    rotaryLeft = pinMode(A1, INPUT);

    rotaryRight = pinMode(A2, INPUT);

 

    slidePot = pinMode(A5, INPUT);

 

    modeSwitch = pinMode(7, INPUT_PULL_UP);

}

 

void loop() {

    // Only runs code if the switch is on.

    if (powerSwitch) {

        // Code is split into two modes, the main "tank" mode

        // and a much simpler "menu" mode.

        if (modeSwitch) {

            if (modeJustSwitched) {menuAccept();}

            tankMode();

        }

        else {

            menuMode();

        }

    }

    else {

        offAnimation();

    }

}

 

// Tank mode takes the inputs from the rotary encoder to

// change a float that then maps to the change of the mouse

// position at a target frame rate.

void tankMode() {

    int rotationDelta = readRotaryEncoder();

    if (rotationDelta > maxTankRotation) rotationDelta = maxTankRotation;

    else if (rotationDelta < -maxTankRotation) rotationDelta = -maxTankRotation;

 

    int potRead = analogRead(slidePot);

 

    currentTankRotation = map (rotationDelta) to (-maxTankRotation, maxTankRotation);

    currentTankSpeed = map (potRead) to (0, maxTankSpeed);

 

    moveMouseBy(currentTankRotation, currentTankSpeed);

}

 

// Turns the recieved values into a vector that determines the next pixel coordinate

// the mouse needs to move to, then stores the new coordinates as the last ones.

void moveMouseBy (int degToRotate, float moveFromPlayer) {

    a bunch of math that takes the current point then draws a vector using the

    angle and velocity of the distance that needs to be moved to using a pixel in

    the middle of the screeen as the origin for a circular graph then maps that value

    to rounded whole numbers stored as newMouseX and newMouseY.

 

    Mouse.moveMouse(newMouseX, newMouseY);

    mouseLastX = newMouseX;

    mouseLastY = newMouseY;

}

 

// Moves options left and right while active. The moment this is turned off, it will

// accept the selected option (different function.)

void menuMode() {

    int rotaryDelta = readRotaryEncoder();

    if (rotaryDelta > 0) {

        Keyboard.pressKey(right);

        delay(50);

    }

    else if (rotaryDelta < 0) {

        Keyboard.pressKey(left);

        delay(50);

    }

    modeJustSwitched = true;

}

 

// Accepts the selected menu option then sets the bool causing

// this function to be called to false.

void menuAccept() {

    Keyboard.pressKey(Enter);

    modeJustSwitched = false;

}

 

// Returns how far the encoder has turned in the time between frames

// as a rounded down int.

int readRotaryEncoder() {

    int rotationDelta;

    for (i = 0; i < frameDelta; i++) {

int readRotaryEncoder() {

    int rotationDelta;

    for (i = 0; i < frameDelta; i++) {

        

        if (rotaryLeft > rotaryRight) {

            lower rotationDelta;

        }

        else if (rotaryRight > rotary) {

            raise rotationDelta;

        }


        delay(1);

    }

    rotationDelta = rotationDelta / frameDelta;

    return rotationDelta;

}


void startUpAnimation() {

    pretty neo-pixel animation with sound when the device is turned on

}


void offAnimation() {

    pulsing red neo-pixels to let you know the device is not working.

}


// Notes:

// Add a function to change neopixels when turning the crank?



No comments:

Post a Comment

Note: Only a member of this blog may post a comment.