Catalogue

Monday, September 26, 2016

Art 3: Base Game Model Recording





For this week's update I recorded myself creating a 3D model that I plan to use in a future project.
At this point in time I was still working on Tank Top Venus, now available to play here.

Regarding the video. I got a bit experimental in a few segments, so it's by no means a speed modeling video. The model being created is meant to be used as a base for other human characters in the game, so its main purpose is flexibility. I chose a female form for the fact that it is easier to transition it into a male form rather than vice versa. Removing features is easier than adding them on.

The model was made using Blender. The main tools I used were: Extrude (E), Fill (F), merge (W/ alt+M), and wide select (C). The initial cube already has a mirror modifier applied to it, as that is the default scene I have set up for myself. For a more detailed explanation of Blender's main tools, I made this video a long time ago explaining them.

Monday, September 19, 2016

Top Venus(name pending) 1: First video & concept art.


Controls: 
Arrow Keys / WASD/ Analogue Stick/ D-Pad- Moves.
Space/ 3/ B- Jumps.
Shift/ R1- Walk.
Q/ L1- Lock on.
Right Mouse/ 0/ 1/ 4/ 5/ X/ Y- Dodge
Left Mouse/ 2/ A- Kick attack.

About the video:

I'm pretty happy with the walking animation, so I show that off for a bit at the start along with the pants on/ pants off feature. The walk was partially inspired by Bayonetta, and shows off Venus' personally pretty well.

After that I show off the clothing removal a bit more. When Venus does not have her wings she can't fly. Her clothing is actually based on her HP. She gains a double jump when her HP is 3, her wings and 2 more midair jumps when it's 4, and has 5 total midair jumps when it's full. You get more clothing as you defeat enemy succubi, seen in their idle state in the video.

If she loses too much clothing her animations change so she covers her chest. This also serves to let the player know that she's lost too much HP, and her abilities have become too limited. At this point you're no longer able to home in on things to kick around, and have to fight with your own power until you can reclaim your clothing & HP.

Which leads me to the next point, Venus' main attacking method: she kicks things into other things. In the video this is represented by a ray that shows where the enemy would fly when kicked. She has a homing feature with this attack, which gains more range as her HP rises.

The last feature is mainly for the debug mode, but it'll be available for the player to use at their discretion: time control. I added this feature partly just for being sexy, and partly to test how well I could implement it. This whole project was mainly to get me into scripting for 3D games, this was my first time scripting for 3D features.  I definitely plan to use what I learned in future projects, whether or not I decide to continue this particular game past the Game Jam's ending.



The initial idea for "Top Venus" was a game about someone who stole people's clothes. At first it was some sort of love god who ran around randomly stripping everyone. But soon I had the idea for the main character also losing her clothing, and even gaining powers based on her outfit. The idea of a succubus who stole her opponent's clothing was pretty appealing to me, though admittedly it's a bit less original than you might know. The initial design for Venus, as seen above, was very appealing to me and brought the idea together.

Monday, September 12, 2016

Scripting 6: Script: 3D_Movement

I've been working on a 3D game recently for Itch.io's Lewd Jam. So here's the Movement script I made, keep in mind that it's my first time scripting for 3D movement. As usual, this is a C# script written in Unity.

There are 25 total variables, 20 of which are public variables. Movement scripts are central, so this amount is pretty normal as a lot of other scripts have to reference this one. 



Movement

    void Start(): Only used to set a few variables


  • LookDir: References the CamReffer object's transform component. CamReffer is an object that mirrors the camera's rotation, but ignores the x rotation.
  • SuCont: References the player object's CharacterController component. This was my first time using this component instead of Rigidbody. I hear there's a lot of benefits to it, but there are some limits that will keep me from using it more in the future, such as being unable to rotate the object's colliders.
  • animRef: References modelRef's Animator_Controller component.
    • modelRef is a public variable that has to be set from the Inspector. It references the object's model, which is parented to this object. As it can have multiple names, I have yet to figure out a way to automate this.
  • camRef: References the camera's parent object  "Camera_Control."
    • The camera in this game is parented to the Camera_Control object, which sticks right to the player. 

    void FixedUpdate(): Only being used for collision checking.
groundCheckPhysics.Raycast(transform.position,Vector3.down,0.05f,1<<0);

    void Update(): Handles the meat of the functions.

  • if(para>0)para+=-1 Time.deltaTime * Master_Script.gameSpeedTimer; else para=0;  
    • Counts down "para." I usually include countdowns in FixedUpdate for accuracy, but I need variables in this game to be manipulated by MasterScript's gameSpeedTimer variable, which is used to control time.
    • Master_Script.gameSpeedTimer controls the speed of the game. Each variable multiplied by this is affected by the time control feature.
  • setLookDir(); is called early on to easily reference the Camera_Control's rotation.
  • movementSpeed.y = 0; so that the movementSpeed Vector3 doesn't control your vertical velocity.
  • if((lookDir.rotation * movementSpeed).sqrMagnitude>maxspeed*10)movementSpeed*=0.95f;
    • This is the speed limiter. If the magnitude of your horizontal movement is greater than the allowed maxspeed, it gets pulled back down by simple multiplication.
//While in the Air
if(!groundCheck)
  • animRef.SetBool("OnGround"false); Tells the animator that you're in the air.
  • if  (!antigrav) jumpSpeed =  Mathf.Lerp(jumpSpeed,gravityset,0.01f * Master_Script.gameSpeedTimer); 
    • This part controls your falling speed. As long as the antigrav variable is turned off, you fall. Some in-game abilities stop your falling, in which case this line is ignored and you will freeze in midair.
  • accelspd = acceleration/50f; accelspd is the active acceleration used for horizontal movement, while acceleration is the character's base acceleration. These two variables are separated for flexibility, such as with this line which is meant to lower your acceleration in midair.

else (groundCheck finds ground underneath you)

  • if(jumpSpeed<0) animRef is told that you've landed on the ground through the "LandTrigger."
  • animRef is also told that you are on the ground constantly through the "OnGround" bool.
  • jumpSpeed is now set to 0 if you're still falling, this ensures the landing trigger only happens once.
  • if(actionMov) toggles wether or not you're holding the shift key.
    • If on, the animator is told and the acceleration and maxspeed are lowered.
    • If off, the speed variables accelspd and maxspeed are set higher.
  • Finally, doubleJumps are reset while you're on the ground.
//Animation variables.
  • It is at this point that the animator is given the movementSpeed and jumpSpeed floats. Horizontal movement is multiplied by the gameSpeedTimer, vertical movement isn't.
  • if(movementSpeed.magnitude ==  0 && jumpSpeed ==0) if you're not moving at all.
  • idleChecks = Random.Range(0, 800); This variable chooses a random number.
    • Now the animator is told what number idleChecks chooses. The animator knows to play a random idle animation based on which number pops up.

//Final Speed Settings.

  • tYMove = Vector3.up * jumpSpeed * 10; this is the final vertical movement.
  • finalSpeed = (movementSpeed * Time.deltaTime) + tyMove/20); 
    • This is the final movement calculation. Horizontal and vertical movements are separated so they don't affect each other.
  • SuCont.Move(finalSpeed * Master_Script.gameSpeedTimer); 
    • This is the final movement, which fed to the object's Character_Controller. finalSpeed is multipled by the gameSpeedTimer so that everything can be controlled by the time controlling function.

//Final Rotations.

  • If you're moving, lookRotation = Quaternion.LookRotation(movementSpeed, lookDir.up); creates a new rotation which looks towards whichever direction you're running in.
  • Then, as long as the character isn't stunned for any reason, the model is rotated with:
    • Quaternion.Lerp(model's rotation, lookRotation, 0.2f * the gameSpeedTimer);

    Public Void mover(int Direct0) function adds speed depending on what button is held.

  • setLookDir(); is called first to ensure the rotation is as accurate as possible.
  • Switch (Direct). Depending on the Direct variable's value, movement is added.
    • If Direct = anywhere between 1 and 4, movementSpeed += lookDir.(right or forward) * (-)accelspd; adds speed to whatever direction is pressed.

    public IEnumerator jumper( int powerdivider = 100, float timeHalt0)

  • The animator is told immediately that the "JumpTrigger" should become active.
  • jumpSpeed = 0; to stop vertical momentum.
  • antigrav = true; to halt vertical descent.
  • yield return new WaitForSeconds(timeHalt * Master_Script.gameSpeedTimer); freezes the coroutine for however long it should, most of the time is ignored as timeHalt is set to 0 by default. This was originally added in for wing flapping, but is right now being considered for removal.
  • antigravfalse; can now fall.
  • jumpSpeed = ((jumpower * 10f)/powerdivider)/10f * ((Master_Script.gameSpeedTimer/15)+1);
    • Sets the jumping movement. It's a little convoluted, but anything out of the ordinary was put there to regulate for either the time controlling variable or varied jump heights.
  • sideWaysRot = Vector3.zero; resets the rotation variable.
Void setLookDir()
  • lookDir.rotation = camRef.transform.rotation; tells lookDir to copy the camera.
  • lookDir.Rotate(-lookDir.eulerAngles.x + sidewaysRot.x, 0, sidewaysRot.z);
    • Reverses the x rotation, so that the camera's up and down angle don't affect motion.
    • Leaves the y rotation alone, this is what we wanted the most.
    • Adds the rotation variable sideWaysRot to LookDir's x and z rotations.

    public IEnumerator rotateToCam(int angleOffX = 0, int angleOffZ = 1, float timeset = 0.1f) This function rotates the model based on the camera's rotation.

  • setLookDir();
  • Vector3 tAngle = Vector3.zero;
    • tAngle += lookDir.right * angleOffX;
    • tAngle += lookDir.forward * angleOffZ;
    • These 3 functions create a new Euler Angle by using the camera's rotation as a base.
  • if(tAngle.magnitude != 0) lookRotation = Quaternion.LookRotation(tAngle.normalized , lookDir.up);
    • if the desired Euler Angle exists, it is converted to the Quaternion LookRotation.
  • int t = 0;
  • while(t<10)
    • modelRef.transform.rotation = Quaternion.Lerp(modelRef.transform.rotation,lookRotation,0.1f * Master_Script.gameSpeedTimer); 
    • t++; 
    • yield return new WaitForSeconds(timeset);
      • All this basically just turns the model 10 times towards the desired angle.

And that's the 3D_Movement script. You'll notice mover() and jumper() aren't called. That's because the actual button presses are handled by a different script, so that the Movement script can be used for enemies as well as the player character. The second script will now be explained below, as originally I used these functions alongside the Movement script.

Player_Control
void Start()
  • playRef = this.gameObject;
  • movRef = playRef.GetComponent<Movement>();
    • References to the player object and Movement script are made.

void Update()
if(!Master_Script.ispaused && para==0) everything under Update only works if the game isn't paused and the character isn't stunned.

  • If the character isn't already moving at maxspeed or you're in the air, and you're not stunned, the mover variables are called through movRef.mover(1-4);
//Slowing Down
  • For both horizontal and vertical movement, if there are not buttons being pressed the movementspeed of either direction is Lerped down to 0.
  • movRef.actionMov is set to wether or not you're holding down the shift key.
//Jumping
  • If you're in the air and holding down the jump key, and you have available doubleJumps and are past a certain falling speed:
    • movRef.StartCoroutine(movRef.jumper(120)); you flap your wings.
    • movRef.doubleJumps+=-1;
  • If you are on the ground && tap the jump key, you jump.
    • movRef.StartCoroutine(movRef.jumper()); 
And that's the Player_Conrol script. I've been having a good time working on this new project in the past week and 1/2, and I should have something to show for it very soon. 

Monday, September 5, 2016

Blog 4: Ludum Dare

I finally have a chance to relax now that the internet's biggest game jam has ended. It was tiring work, and the end result is still very unfinished, but I know each of us feels prideful in our work. I'm going to be writing a little bit about my experiences here. This project was my first "finished" game project, my first Ludum Dare game, and my first time leading a group project. It was also my first time getting headaches, chest aches, and stomach aches all at the same time.

If you're on Windows you can download the game here, or play the web version here. If you don't see 3 vines on the downloadable version, try a different resolution. Something 16:9.

The planning was not very good, and in the end it turned into a real rush job. After the initial time limit, it turns out that Ludum Dare gives you an extra hour to submit your entries. We weren't able to take advantage of the extra time however, so we just barely managed to finish and turn it in. We'll probably be fixing the game up at a future time, but for now we'll just leave it as is.

It was a 4 person team of me, Kino, Skewer, and Omoroi. Since it was my decision to take part in the Ludum Dare, I was manager. I basically acted as leader and filled in whatever roll was missing. Kino was the lead programmer, he put together the platformer logic that let me edit the scripts later on to fit the vision. Pineappleskewer was the lead artist, she drew the idle enemy sprites as well as the background and splash art. And Omoroi was the advisor who helped in planning and scheduling. It was thanks to everyone's hard work that we were able to finish the game at all. After we were finished, everyone was excited to get to work on a new game jam project. Our first jam didn't go very smoothly at all, but the experience will be invaluable to us in the future.

As time went on I grew more and more impatient, and I know the rest of the team noticed. In my defense, I had spent over 12 hours animating sprites at this point (I find pixel art frustrating to make),  and took a very quick tutorial on using Git in order to help with the scripting. Not to mention the entire time we were a day behind schedule. Those three days were so stressful that some of us had practically spent the entire next day just sleeping. Forgive me if I vent a little even while I'm writing this.

This Ludum Dare's theme was "Ancient Technology." The first idea was to use caveman tech, where inspiration was taken from these videos, but that didn't stick. We started tossing around concepts, and eventually the idea just came to me: "You're a cowboy with a revolver that somehow goes back in time. You shoot a guy, and now everyone worships you, but things start going wrong. You only have 5 bullets, and you need to help your worshippers win a war." Later on I began putting more emphasis on jumping on enemies, but Skewer suggested a whip which led to what we have now. The end result ended up more like a puzzle game crossed between Indiana Jones and Castlevania. Though the revolver idea didn't get implemented the name still references his gun, it's also a reference to Chrono Trigger.

What I learned:
We didn't plan very well at all. Some group members had other things going on during those 3 days that we couldn't plan ahead for. There was also too much put into the graphical work. My time was basically split 50/50 as I spent the entire first day animating the cowboy, half of the second day creating objects and animating enemies, and half the second and all of the third day was spent scripting and exporting. We could have fixed up all the glitches if I focused more on the programming, or maybe even have implemented more than one level. That's the point of a first outing though, I'll know more on the second attempt. Overall I'm happy with Time Trigger as a concept, and hope to continue work on it when I have more free time.



This last image works pretty much as a guide to the level. It was created to act as our final piece of concept art, where the idea was simply to recreate what was in the image. We succeeded in that, as this image can be used as a guide for completing the game.