aha
This commit is contained in:
@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.Examples.BilliardsPredicted
|
||||
{
|
||||
// example input for this exact demo.
|
||||
// not general purpose yet.
|
||||
public struct PlayerInput
|
||||
{
|
||||
public double timestamp;
|
||||
public Vector3 force;
|
||||
|
||||
public PlayerInput(double timestamp, Vector3 force)
|
||||
{
|
||||
this.timestamp = timestamp;
|
||||
this.force = force;
|
||||
}
|
||||
}
|
||||
|
||||
public class PlayerPredicted : NetworkBehaviour
|
||||
{
|
||||
// white ball component
|
||||
WhiteBallPredicted whiteBall;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
// find the white ball once
|
||||
#if UNITY_2022_2_OR_NEWER
|
||||
whiteBall = FindAnyObjectByType<WhiteBallPredicted>();
|
||||
#else
|
||||
// Deprecated in Unity 2023.1
|
||||
whiteBall = FindObjectOfType<WhiteBallPredicted>();
|
||||
#endif
|
||||
}
|
||||
|
||||
// apply force to white ball.
|
||||
// common function to ensure we apply it the same way on server & client!
|
||||
void ApplyForceToWhite(Vector3 force)
|
||||
{
|
||||
// https://docs.unity3d.com/2021.3/Documentation/ScriptReference/Rigidbody.AddForce.html
|
||||
// this is buffered until the next FixedUpdate.
|
||||
|
||||
// get the white ball's Rigidbody.
|
||||
// prediction sometimes moves this out of the object for a while,
|
||||
// so we need to grab it this way:
|
||||
Rigidbody rb = whiteBall.GetComponent<PredictedRigidbody>().predictedRigidbody;
|
||||
|
||||
// AddForce has different force modes, see this excellent diagram:
|
||||
// https://www.reddit.com/r/Unity3D/comments/psukm1/know_the_difference_between_forcemodes_a_little/
|
||||
// for prediction it's extremely important(!) to apply the correct mode:
|
||||
// 'Force' makes server & client drift significantly here
|
||||
// 'Impulse' is correct usage with significantly less drift
|
||||
rb.AddForce(force, ForceMode.Impulse);
|
||||
}
|
||||
|
||||
// called when the local player dragged the white ball.
|
||||
// we reuse the white ball's OnMouseDrag and forward the event to here.
|
||||
public void OnDraggedBall(Vector3 force)
|
||||
{
|
||||
// apply force locally immediately
|
||||
ApplyForceToWhite(force);
|
||||
|
||||
// apply on server as well.
|
||||
// not necessary in host mode, otherwise we would apply it twice.
|
||||
if (!isServer) CmdApplyForce(force);
|
||||
}
|
||||
|
||||
// while prediction is applied on clients immediately,
|
||||
// we still want to validate every input on the server and reject it if necessary.
|
||||
// this way we can latency free yet cheat safe movement.
|
||||
// this should include a certain tolerance so players aren't hard corrected
|
||||
// for their local movement all the time.
|
||||
// TODO this should be on some kind of base class for reuse, but perhaps independent of parameters?
|
||||
bool IsValidMove(Vector3 force) => true;
|
||||
|
||||
// TODO send over unreliable with ack, notify, etc. later
|
||||
[Command]
|
||||
void CmdApplyForce(Vector3 force)
|
||||
{
|
||||
if (!IsValidMove(force))
|
||||
{
|
||||
Debug.Log($"Server rejected move: {force}");
|
||||
return;
|
||||
}
|
||||
|
||||
// apply force
|
||||
ApplyForceToWhite(force);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79c6ff7781e8c4174b07ea7e905f62b2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Examples/BilliardsPredicted/Player/PlayerPredicted.cs
|
||||
uploadId: 736421
|
@ -0,0 +1,66 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &7529115942715260948
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1534822232247476853}
|
||||
- component: {fileID: 1279227879756014839}
|
||||
- component: {fileID: -1702612885512365273}
|
||||
m_Layer: 0
|
||||
m_Name: PlayerPredicted
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &1534822232247476853
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7529115942715260948}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &1279227879756014839
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7529115942715260948}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
sceneId: 0
|
||||
_assetId: 4028108460
|
||||
serverOnly: 0
|
||||
visibility: 0
|
||||
hasSpawned: 0
|
||||
--- !u!114 &-1702612885512365273
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7529115942715260948}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 79c6ff7781e8c4174b07ea7e905f62b2, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
syncDirection: 0
|
||||
syncMode: 0
|
||||
syncInterval: 0
|
@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ffab5af8b8b354f8ca8eb7aeaf89dfac
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Examples/BilliardsPredicted/Player/PlayerPredicted.prefab
|
||||
uploadId: 736421
|
Reference in New Issue
Block a user