Hey! I don't have time to make a pretty looking page about this game yet, so this post will have to do for now.
Here you can see a video of me playing the game on a NS dev kit. The cardboard is to hid the secret stuff, and I didn't have the best position for recoding, so I apologize for that.
Nonetheless! Let me explain what you see here. This game is essentially a Super Monkey Ball clone, or at least that was the idea going in. So lets talk about the two major sections of the game.
This game was made as a two person team and we each were given out own sections. Level creation was done by Kenneth Onulak Jr, and the gameplay was done by me.
Level creation:
Here the level editor is entirely controlled by the touch screen using gestures. You are able to select what to "draw" onto the level grid and then tap and drag to place the tiles.
One thing to note is that the walls, goal, and player are on a different layers than the floors/blocks so they can overlap.
Gameplay:
Let me just paste the readme for the controlls here.
____Controls____
--In Level--
Gyro: Tilt level
+/-: Restart level
Left Stick:
Up/Down: Increase/Decrease camera pitch
Right Stick:
Up/Down: Zoom In/Out
Left/Right: Rotate camera (yaw) Left/Right
Gyro:
Here you can see that the player movement is controlled fully by gyro controls. When the controller is tilted, the level is tilted the same as the controller. However the camera is a child of the level so you don't see the level tilt, but it still is if you pay close attention to the background.
Because of this the player, that is just a sphere, rolls down the slope and that acts as the play movement.
The difficult thing to manage is the camera rotation, as the players perspective changes, so the level tilt should also change. So what I had to do was counteract the camera rotation from the world rotation.
Here is a code snip-it of a function called when the gyro is updated. `rot` here is a Vector2 of the `sixAxisState.direction` after some conversions to get the rotation around the x and z axis.
(Apologies for the compact code)
Vector2 clampedRot = new(
Mathf.Clamp(rot.y, -pitchRollTiltLimit, pitchRollTiltLimit),
Mathf.Clamp(rot.x, -pitchRollTiltLimit, pitchRollTiltLimit));
// Start with the cameras yaw rotation
worldPivot.localRotation = Quaternion.Euler(0, cam.yawRotation, 0);
// Multiply in the gyro rotation
worldPivot.localRotation *= Quaternion.Euler(
clampedRot.x.Remap(-pitchRollTiltLimit,
pitchRollTiltLimit, -pitchRollInterpMax, pitchRollInterpMax),
0,
clampedRot.y.Remap(-pitchRollTiltLimit,
pitchRollTiltLimit, -pitchRollInterpMax, pitchRollInterpMax));
// Undo the cameras yaw rotation
worldPivot.localRotation *= Quaternion.Euler(0, -cam.yawRotation, 0);
public static class ExtensionMethods {
public static float Remap(this float value,
float from1, float to1,
float from2, float to2) {
return (value - from1) / (to1 - from1) * (to2 - from2) + from2;
}
}
Essentially what is happening here is, I set the worlds yaw rotation to the cameras, multiply in the gyro rotation, then undo the cameras yaw rotation. This is done because when the world is rotated to face the camera first, and we put the gyro on after, the gyro rotation is done relative to the set rotation. That way the players doesn't need to move the controller with the camera as it is rotation to keep the same relative rotation. (Ie. when the player is tilting forward, they expect the tilt to be relative to where they are looking at, not some pre set direction that is forward.) And when we are done adding the gyro rotation we remove the camera rotation so the world stays facing the same "forward" direction.
Rumble:
As the marble moves faster the rumble of the controller gets stronger. Additionally, based on the direction you are moving in relative to the camera, which side of the controller rumbles more than the other.
Here is a code sample for this:
// Calc percent of vibration for left/right controller
Vector2 vibPercent;
// Get the left/right rotation and divide by max to get the rotation
// percent from 0-1
vibPercent.x = CameraController.Instance.worldRotRollOffset /
CameraController.Instance.maxCameraRollTilt;
vibPercent.y = vibPercent.x;
// Convert value range so negative affects the left (x) more and positive
// the right (y) more controller
// ie a value of -.12 would look like L: 100% R: 88%
if (vibPercent.x <= 0)
vibPercent.x = 1;
else
vibPercent.x = Mathf.Lerp(1, 0, vibPercent.x);
if (vibPercent.y >= 0)
vibPercent.y = 1;
else
vibPercent.y += 1;
/* Ref constant for snip-it
[Tooltip("Frequency is in hz, so range is around 1 to 1000")]
private Vector2 vibrationFrequencyRange = new(1.0f, 200.0f);
private Vector2 vibrationAmplitudeRange = new(0.0f, 0.9f);
*/
float freq = Mathf.Lerp(vibrationFrequencyRange.x, vibrationFrequencyRange.y, vol);
vib.frequencyLow = freq * vibPercent.x;
vib.frequencyHigh = freq * vibPercent.x;
// vib is a nn.hid.VibrationValue sent to the controller
float amp = Mathf.Lerp(vibrationAmplitudeRange.x,
vibrationAmplitudeRange.y, vol);
vib.amplitudeLow = amp * vibPercent.x;
vib.amplitudeHigh = amp * vibPercent.y;
Sound:
In addition to the rumble the sound of the marble rolling gets louder the faster you roll. Where the vibration gets the tilt, the sound references the marbles velocity. Both methods could have worked in either scenario, but I guess this is what I went with.
Sticks:
I think this part is fairly simple, you get the just from the control explanation.
Well that is all I got time for right now. Thanks for reading this and hopefully I get to writing a proper page for this little project!
Comments