3D sandbox game development log - 1

Keywords: C# Unity Unity3d

preface

background

  study unity And other related content (game server, lua,shader It has been a year, and c++The level of algorithm is also in this year
 It's time to patiently make a qualified game to summarize and refine myself, because I have only spare time and I'm the only one at present,
Therefore, the development of the project is expected to last for at least 4 months. The goal is to send the game to steam (after all, the games we used to play were only sent to friends)
  All the code for this project, shader Even some art works will be done from scratch as much as possible, without referring to some old code or ready-made plug-ins,
And we will try our best to ensure the standardization of the code and clear comments. This log will be kept every three days (mainly to see if there is anything new). It will be recorded here
 Record all the difficulties and knowledge I encountered in the development process.

About games

  The main reason for choosing the sandbox type is that the sandbox is a stand-alone type (because it is not sure that the server will be developed). In the type, the playability is relatively high and the investment is relatively small (compared with Drama)
Love is a kind of game, and relative fps I also have low requirements for art, and I have been playing famine recently, which has given me some basic inspiration.
In addition, there is almost no further idea. I will invite my friends to try and listen to their opinions at any time. After all, without planning experience, I can only take one step at a time.
Therefore, the log will also contain modifications to a large number of existing code (this is not a tutorial!)

journal

  The first is a third person character controller. I'm going to use all player scripts controller Name, and then separate various modules as much as possible to
 Facilitate follow-up development, so I wrote separately LocomotionController and ViewController To control the movement and perspective of characters, and put them aside for the time being
 Put on Player namespace in


LocomotionController hasn't written anything yet. It's the most basic movement. The character model hasn't been determined yet. It doesn't involve much animation. I mainly spent a lot of effort on the perspective, because I think the comfort of the perspective is very important.

Achieve a smoother view following delay effect

At the beginning, I wanted to achieve a smoother perspective follow delay effect. As the character moves, the perspective pulls back and gradually recovers when stationary; Because you can't use virtual camera, this part is the main content of this time.

camera as a character sub object

At first, I wanted to separate the camera from the sub object of the character, because if the sub object is always affected by the character itself. However, I later found that although this can highly customize the movement of the camera, it is prone to bug s. The camera must monitor all the movement and direction of the character. When various items and monsters are added in the later stage, the character will also have forced movement and unpredictable movement, so I finally decided to set the camera as a sub object of the character
This means that all our operations should be carried out in the coordinate system of the parent object, using localPos, etc.

public float followDistance;//Standard following distance
public float maxFollowDistance;//Maximum following distance
public float followSpeed;//The speed at which the camera pulls back when moving
public float resumeSpeed;//The speed at which the camera recovers when stationary
void UpdateCamPos()
{
    //Simulate the effect of camera delay following
    //Note that relative positions are used here, which will not cause bug s when the character receives external force movement, so followDistance and maxDistance should also be set to relative positions
    //followSpeed and resumeSpeed determine the speed at which the camera advances when running and when stationary
    //Interpolate from the current position to the maximum / minimum position
    float currentDistance = Vector3.Magnitude(thirdPersonCam.transform.localPosition);
    float distance;
    if(Input.GetKey(KeyCode.W))
    {
        distance = Mathf.Lerp(currentDistance, maxFollowDistance, Time.deltaTime * followSpeed);
    }
    else
    {
        distance = Mathf.Lerp(currentDistance, followDistance, Time.deltaTime * resumeSpeed);
    }
    thirdPersonCam.transform.localPosition = thirdPersonBackCamDir.normalized * distance;        
}

Since you want a smooth transition, you must use lerp interpolation, because it seems that you can't directly find points for vector3 interpolation, so I interpolate the distance (modulus length) after determining the direction. Note that you should use the current distance interpolation instead of directly interpolating between followDistance and max, otherwise you will have problems if you stop before reaching the maximum distance.

Angle of view rotation

Rotation of viewing angle at present, I intend to select a mouse with certain restrictions to control the free viewing angle, obtain mouse input for rotation, and use clamp to limit

public float rotateSpeed;//Angle of view steering speed
public float lookDownThreold;//Maximum angle to look down
public float lookUpThreshold;//Maximum angle to look up
public float lookAroundRange;//Maximum angle to look left and right
void UpdateCamRot()
{
    //Left and right rotation angle
    float rotateX = -Input.GetAxis("Mouse Y");
    float rotateY = Input.GetAxis("Mouse X");
    //Set localeuler angles directly or rotate x and then y to avoid the problem of rotation around the z axis caused by Euler rotation
    //Note that the angle range obtained is 0-360. To achieve centrosymmetric clamp, you need to translate
    float currentX, currentY;
    if(thirdPersonCam.transform.localEulerAngles.x >= 180) currentX = thirdPersonCam.transform.localEulerAngles.x - 360;
    else currentX = thirdPersonCam.transform.localEulerAngles.x;
    if(thirdPersonCam.transform.localEulerAngles.y >= 180) currentY = thirdPersonCam.transform.localEulerAngles.y - 360;
    else currentY = thirdPersonCam.transform.localEulerAngles.y;
    float angX = Mathf.Clamp(currentX + rotateX * rotateSpeed * Time.deltaTime, lookDownThreold, lookUpThreshold);
    float angY = Mathf.Clamp(currentY + rotateY * rotateSpeed * Time.deltaTime, -lookAroundRange, lookAroundRange);
    thirdPersonCam.transform.localEulerAngles = new Vector3(angX, angY, 0);
    //thirdPersonCam.transform.Rotate(x, 0, 0, Space.Self);
    //thirdPersonCam.transform.Rotate(0, y, 0, Space.World);
}

There are two main problems here. The first is that the angle value is obtained in the range of 0-360. There is no way to connect my left and right or up and down (from negative to positive). clamp has a very strange effect, which must be handled first
The second is the Euler rotation problem I knew for a long time but forgot when writing. If the rotation is written as Rotate (x, y, 0), there will be unexpected rotation around the Z axis, mainly because the Euler rotation order is zxy, and the coordinate system will be rotated at the first rotation, As a result, the second rotation (i.e. rotation around y) will not Rotate around the "Y-axis we want", but the y-axis after the first rotation, and finally the z-coordinate has changed. The correct way to write is code and comments

View switching

In this case, there is a problem that I can't see the front of the character, so I made another viewing angle (similar to gta) and put the camera in front of the character

public Vector3 thirdPersonBackCamDir;//Third person default camera orientation (relative to person)
public Vector3 thirdPersonFrontCamPos;//Third person front camera position
public Transform lookAt;//Fixation position
void ChangeViewAspect()
{
    switch(viewtype)
    {
        case ViewType.ThirdPersonBack:
            viewtype = ViewType.ThirdPersonFront;
        break;
        case ViewType.ThirdPersonFront:
            viewtype = ViewType.ThirdPersonBack;
        break;
    }
    SetCamTransform(viewtype);
}
void SetCamTransform(ViewType vt)
{
    switch(viewtype)
    {
        case ViewType.ThirdPersonBack:
            thirdPersonCam.transform.localPosition = thirdPersonBackCamDir.normalized * followDistance;
        break;
        case ViewType.ThirdPersonFront:
            thirdPersonCam.transform.localPosition = thirdPersonFrontCamPos;
        break;
    }
    thirdPersonCam.transform.LookAt(lookAt);
}

I'm not going to join the first person for the time being.
At the same time, the front view will prohibit the angle of view rotation and camera scaling (only hard follow)

void FixedUpdate()
{
    if(viewtype == ViewType.ThirdPersonBack)
    {
        UpdateCamPos();
        UpdateCamRot();
    }
    if(Input.GetKeyDown(KeyCode.V)) ChangeViewAspect();
}

Both character movement and camera movement are placed in fixedupdate

Final effect

Posted by timmah22 on Fri, 03 Sep 2021 11:44:15 -0700