I have written a lot about coding and game design. I have re-posted some of that writing on this page.
I was prototyping a game in unity, a top down shooter. The game was in a really early state, but I thought I had all of controls working, the player could move and shoot, their character would turn towards the mouse and the camera would follow the player around. However something already seemed really wrong with the game. The game felt super claustrophobic like everything was zoomed too far in. I set the camera view to be a bit larger. The game didn't feel zoomed in any more but now everything was tiny.
I eventually realized that my issue wasn't the how much I could fit on screen at once. The issue was what was on screen. There is a trick that gets used a lot in top down shooters, instead of centering the camera directly over the player character's head the camera is centered somewhere in front of the player. This camera behavior is the type of mechanic that a player might not even realize is in a game. However it can have a huge impact on how the game plays. Players point their mouse where the action is happening in top down shooters. If there is an enemy they want to shoot they point their mouse at it. If there is a new room they want to explore they point there mouse at it. This camera trick lets the player keep more of the things that are interesting on screen. It also causes that less of the screen to be taken up by the empty room that the player just left. Centering the camera in front of the player also makes the game play easier. Players can see and shoot at enemies from farther away then they could with a zoomed in camera. The enemies appear larger on screen and are easier targets then they would be with a zoomed out camera. I realized that this was a mechanic that my game needed. So I tried to implement it. My first idea was to set the camera to always be a fixed distance in front of the player character. When the mouse position was far away from the player this worked fine, but as soon as I moved the mouse close to the player character I discovered a bug. The player would spin around in circles. The player character was programmed to always look at the mouse, and the camera was always centered in front of the player. This meant that there was a point where the player would turn to face the mouse, moving the camera with them when they turned, which would move the mouse's position relative to the player which would cause the player to turn more. I scrapped that code and started over. This time I set the camera to always hover mid way between the player character's position and the mouses position. This strategy came with a new bug. Now the camera worked as intended when the mouse was close to the player, but the camera would fly off away from the player if the mouse was near the edge of the screen. This bug had an easy fix. I set a maximum distance that the player should ever be from the camera. If half the distance from the player to the mouse was less then that maximum distance. Then the camera would center midway between the player and the mouse. If not then the camera would center exactly the maximum distance in front of the player. I finally had the camera behaving in the way I wanted. Here is the c# code that controls the camera position. Vector2 mousePos = Camera.main.ScreenToWorldPoint (Input.mousePosition); Vector2 playerPos = new Vector2 (player.position.x, player.position.y); if ((Vector2.Distance (mousePos,playerPos )/2,playerPos) <= maxDis ) { target.transform.localPosition = new Vector3 (Vector2.Distance (mousePos, playerPos) / 2 , 0, 0); } else { target.transform.localPosition = new Vector3(maxDis,0,0); } transform.position= newVector3(target.transform.position.x,target.transform.position.y,- 10);
0 Comments
Rust is one of my favorite games. There is a lot that it does better than most other games. It is however a flawed game. By flawed I am not referring to the poor optimization or the many glitches.
To understand the biggest problem with Rust, one needs to first understand what it does better than other games. It is unique among MMOs in that it is exciting to farm. In most MMOs players will spend the majority of their time doing low excitement, low risk tasks over and over again in order progress (AKA farming). Farming in Rust is different. It puts players on the edge of their seats. In Rust any player can attack anyone at any time, and if a player dies others can take everything they are carrying. Resources are spread out and deplete when they are farmed. So it is more time efficient to stay out for a long time to gather a lot in one run, rather than farming a little storing what was farmed, then going out again to farm a little more. Farming also makes noise which lets other players know that there is someone gathering resources nearby, someone who might be easy prey. This combination of game mechanics encourages players to stay out on long farming runs, gathering more and more and increasing the amount that they will lose if they die. It also rewards players for hunting down and fighting other players. The result is that farming players are always on the edge of their seat. The longer a farming run lasts the higher the stakes become. This is why farming in Rust isn’t a boring grind, but rather an exciting part of the game. So that’s what Rust does well, a boring mechanic in other games is exciting in Rust. However there is a mechanic in Rust that is boring and doesn’t need to be. Crafting, is extremely tedious in rust. Crafting in Rust takes a long time. Want to craft an axe, be ready to wait 60 seconds. Want to craft a gun, full set of armor, clothes, bullets and medical supplies, all of that be ready to wait 15 minutes. The game allows players to do other things while they craft, but it discourages it. If a player gets in a fight while their weapon or armor is still being crafted they are still risking that item, but won’t gain the benefits of having it for the fight. This encourages players to just sit in their house while they wait for these items to craft. This doesn’t just cause boring gameplay, it causes no gameplay. It isn’t uncommon for players to just tab out of Rust and do something else while they wait for their crafting queue to finish. In my opinion that is the biggest failures that a game can have, to be so uninteresting that players choose to do something else. The fix for this is obvious, make items craft instantly. Yes this could initially cause some balancing issues, but those can be fixed (example: some cheap items that have a long crafting time might need to have their costs increased to avoid having them spammed). It is ironic that one of the game’s biggest strengths is that it’s developers managed to make a normally boring task exciting, but it’s biggest weakness is that the developers made another mechanic needlessly boring. When making networked games in unity new developers tend to run into one issue in particular. Movements that are not handled locally tend to stutter. This issue is caused by the rate at which unity sends updated to other machines through the network transform component. Transforms are updated on other machines a maximum of 30 times a second. Often due to less than perfect connections they end up getting updated even less frequently than that. Even with 30 updates to transform a second movement can still appear jumpy, especially of other elements are moving at a much higher frame rate.
There are two fixes to this, the first is to set the NetworkTransform sync mode to rigidbody (or rigidbody2D). This alone won’t solve the stutter movement issue, you may also need to alter the way that movement is handled in code. Rather then setting a constant velocity when the player hits a key (or when an AI choses to change direction) instead increase or decrease the current velocity towards a maximum. This doesn’t mean that velocity changes need to be slow, as long as it takes a GameObject longer to reach its full velocity than it takes for the NetworkTransform to update that objects position and speed the game will appear smooth. Example... if(Input.GetKey(KeyCode.D)){ if(rb2d.velocity.x < maxSpeed){ b2d.velocity = new Vector2 (rb2d.velocity.x + (moveSpeed * Time.deltaTime),rb2d.velocity.y); } } The other method is to lerp movement using vector3.lerp. Lerp can be used to smoothly interpolate between the last two transforms on each of the clients. This makes it appear as though the position is being updated every frame of the game, even though it is not. Example... If (!isLocalPlayer) { playerTransform.position=Vector3.Lerp(playerTransform.position,syncPos,Time.deltaTime*lerpRate); In this case The syncPos variable should be passed from the local player to the server using a [Command] and back to the other clients using a [ClientCallback]. One downside of the first method is that it limits the way that developers can implement movement. It also makes non local gravity appear floaty. However it can be implement with out causing the clients to have any additional delay (other than what their internet and the network update rate already cause). An upside of the second method is that it allows any form of movement, not just movement by acceleration. The second method also makes movement by gravity appear natural. However it can cause players to see non local positions as what they were a few frames ago, instead of what they truly are on the server. |