[Add] FishNet

This commit is contained in:
2026-03-30 20:11:57 +07:00
parent ee793a3361
commit c22c08753a
1797 changed files with 197950 additions and 1 deletions
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 941f6f8cbac7e9d418ffc6f22ee9359f
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Demos/Prediction/CharacterController/CharacterController
Prediction Demo.unity
uploadId: 866910
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bdae6fa333d2d94449c27856e21d936a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,80 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: BlueMat
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _BumpScale: 1
- _Cutoff: 0.5
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 1
- _Glossiness: 0.5
- _GlossyReflections: 1
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _UVSec: 0
- _ZWrite: 1
m_Colors:
- _Color: {r: 0, g: 0.363446, b: 0.5471698, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
m_BuildTextureStacks: []
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: a2ca0973c8f8a4846bb7f3894c3bc5cf
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Demos/Prediction/CharacterController/Materials/BlueMat.mat
uploadId: 866910
@@ -0,0 +1,80 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: OrangeMat
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _BumpScale: 1
- _Cutoff: 0.5
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 1
- _Glossiness: 0.5
- _GlossyReflections: 1
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _UVSec: 0
- _ZWrite: 1
m_Colors:
- _Color: {r: 1, g: 0.4404881, b: 0, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
m_BuildTextureStacks: []
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: f04f4c9957a2bf340b9fea1547c7ce0b
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Demos/Prediction/CharacterController/Materials/OrangeMat.mat
uploadId: 866910
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 50d54accc2af0c746b0729b097981b93
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,330 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &303449598114786579
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 303449598114786577}
- component: {fileID: -2060332294883903109}
- component: {fileID: 201277550}
- component: {fileID: 4148834954576211901}
- component: {fileID: 8534672635240368605}
m_Layer: 0
m_Name: CharacterControllerPrediction
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &303449598114786577
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598114786579}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -4.80351, y: 0.18147132, z: 5.430528}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6952090537135875148}
- {fileID: 7416592246441027450}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &-2060332294883903109
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598114786579}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 53a6d84aa5d214e48a7308646e5d20da, type: 3}
m_Name:
m_EditorClassIdentifier:
_componentIndexCache: 0
_addedNetworkObject: {fileID: 201277550}
_networkObjectCache: {fileID: 201277550}
_tickCallbacks: 6
_jumpForce: 13
_moveRate: 4
--- !u!114 &201277550
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598114786579}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3}
m_Name:
m_EditorClassIdentifier:
<IsNested>k__BackingField: 0
WasActiveDuringEdit: 0
WasActiveDuringEdit_Set1: 1
<ComponentIndex>k__BackingField: 0
<PredictedSpawn>k__BackingField: {fileID: 0}
<PredictedOwner>k__BackingField: {fileID: 0}
NetworkBehaviours:
- {fileID: -2060332294883903109}
- {fileID: 8022627623488540705}
- {fileID: 2367975056509727580}
InitializedParentNetworkBehaviour: {fileID: 0}
InitializedNestedNetworkObjects: []
RuntimeParentNetworkBehaviour: {fileID: 0}
RuntimeChildNetworkBehaviours: []
_isNetworked: 1
_isSpawnable: 1
_isGlobal: 0
_initializeOrder: 0
_preventDespawnOnDisconnect: 0
_defaultDespawnType: 0
_initializedTimestamp: -8584312066425166658
NetworkObserver: {fileID: 0}
_enablePrediction: 1
_predictionType: 0
_localReconcileCorrectionType: 2
_graphicalObject: {fileID: 0}
_detachGraphicalObject: 0
_enableStateForwarding: 1
_networkTransform: {fileID: 0}
_ownerInterpolation: 2
_ownerSmoothedProperties: 255
_adaptiveInterpolation: 0
_spectatorSmoothedProperties: 255
_spectatorInterpolation: 2
_enableTeleport: 0
_teleportThreshold: 1
<PrefabId>k__BackingField: 7
<SpawnableCollectionId>k__BackingField: 0
<AssetPathHash>k__BackingField: 8652468816125699328
SceneId: 0
SerializedTransformProperties:
Position: {x: -4.80351, y: 0.18147132, z: 5.430528}
Rotation: {x: 0, y: 0, z: 0, w: 1}
Scale: {x: 1, y: 1, z: 1}
IsValid: 1
--- !u!143 &4148834954576211901
CharacterController:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598114786579}
m_Material: {fileID: 0}
m_IsTrigger: 0
m_Enabled: 1
serializedVersion: 2
m_Height: 2
m_Radius: 0.5
m_SlopeLimit: 45
m_StepOffset: 0.3
m_SkinWidth: 0.08
m_MinMoveDistance: 0.001
m_Center: {x: 0, y: 1, z: 0}
--- !u!136 &8534672635240368605
CapsuleCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 303449598114786579}
m_Material: {fileID: 0}
m_IsTrigger: 0
m_Enabled: 1
m_Radius: 0.5
m_Height: 2
m_Direction: 1
m_Center: {x: 0, y: 1, z: 0}
--- !u!1 &2693313506199881053
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7416592246441027450}
- component: {fileID: 1810266687591957409}
- component: {fileID: 2367975056509727580}
m_Layer: 4
m_Name: SphereCollider
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &7416592246441027450
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2693313506199881053}
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_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 303449598114786577}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!135 &1810266687591957409
SphereCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2693313506199881053}
m_Material: {fileID: 0}
m_IsTrigger: 1
m_Enabled: 1
serializedVersion: 2
m_Radius: 0.3
m_Center: {x: 0, y: 0.26, z: 0}
--- !u!114 &2367975056509727580
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2693313506199881053}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3dc97a8b9e628044e83004c93095c14d, type: 3}
m_Name:
m_EditorClassIdentifier:
_componentIndexCache: 2
_addedNetworkObject: {fileID: 201277550}
_networkObjectCache: {fileID: 201277550}
MaximumSimultaneousHits: 16
AdditionalSize: 0.1
Layers:
serializedVersion: 2
m_Bits: 1
--- !u!1 &5873068582516782542
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6952090537135875148}
- component: {fileID: 6605329024375725182}
- component: {fileID: 9006911641345807741}
- component: {fileID: 8022627623488540705}
m_Layer: 0
m_Name: Capsule
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &6952090537135875148
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5873068582516782542}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 1, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 303449598114786577}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &6605329024375725182
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5873068582516782542}
m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &9006911641345807741
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5873068582516782542}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!114 &8022627623488540705
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5873068582516782542}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 453481867b26f7c43b5bf38802a5f50e, type: 3}
m_Name:
m_EditorClassIdentifier:
_componentIndexCache: 1
_addedNetworkObject: {fileID: 201277550}
_networkObjectCache: {fileID: 201277550}
_initializationSettings:
TargetTransform: {fileID: 303449598114786577}
DetachOnStart: 0
AttachOnStop: 0
_controllerMovementSettings:
EnableTeleport: 0
TeleportThreshold: 0
AdaptiveInterpolationValue: 0
InterpolationValue: 2
SmoothedProperties: 4294967295
SnapNonSmoothedProperties: 0
_favorPredictionNetworkTransform: 1
_spectatorMovementSettings:
EnableTeleport: 0
TeleportThreshold: 0
AdaptiveInterpolationValue: 0
InterpolationValue: 2
SmoothedProperties: 4294967295
SnapNonSmoothedProperties: 0
@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 5b712878ecece354ba4ffb026c0a221c
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Demos/Prediction/CharacterController/Prefabs/CharacterControllerPrediction.prefab
uploadId: 866910
@@ -0,0 +1,17 @@
This demo shows how to use a character controller with our prediction system.
Important:
* Beta ReplicateStates must be enabled to use this demo. You can toggle this setting using the Fish-Networking menu.
Setup:
* Open demo scene.
* Start server, host, or client only. You may test with parrelSync or host the project.
Notes:
* Server and all clients run the same inputs. This is done by using State Forwarding on the NetworkObject inspector.
* NetworkTrigger is used to attach to platforms. You may review code within the controller script to see attach logic and comments.
On the prefab the trigger is attached as a child of the NetworkObject for sorting, but not under the graphical, as we do
not want the trigger to be modified outside the tick system (graphical gets smoothed, and is updated outside the tick loop).
* Moving platform predicts fully into the future so the client may step on it in real-time. See notes in MovingPlatform script.
Spectated objects may appear to correct when moving onto the platform. This can be resolved by balancing the future
prediction amount on the platform and the spectated objects.
@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 242afb68b5bce2f40ab2da029c12d008
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Demos/Prediction/CharacterController/ReadMe.txt
uploadId: 866910
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: eaf414b899169f04bb4b0d65424a40bf
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,499 @@
using System;
using FishNet.Component.Prediction;
using FishNet.Connection;
using FishNet.Object.Prediction;
using FishNet.Transporting;
using FishNet.Utility.Template;
using UnityEngine;
namespace FishNet.Demo.Prediction.CharacterControllers
{
public class CharacterControllerPrediction : TickNetworkBehaviour
{
#region Types.
/// <summary>
/// One-time inputs accumulated over frames between ticks.
/// </summary>
public struct OneTimeInput
{
/// <summary>
/// True to jump.
/// </summary>
public bool Jump;
/// <summary>
/// Unset inputs.
/// </summary>
public void ResetState()
{
Jump = false;
}
}
public struct ReplicateData : IReplicateData
{
public ReplicateData(Vector2 input, bool run, OneTimeInput oneTimeInputs)
{
OneTimeInputs = oneTimeInputs;
Input = input;
Run = run;
_tick = 0;
}
/// <summary>
/// True if Jump input was pressed for the tick.
/// </summary>
public OneTimeInput OneTimeInputs;
/// <summary>
/// Current movement directions held.
/// </summary>
public Vector2 Input;
/// <summary>
/// True if run is held.
/// </summary>
public readonly bool Run;
/// <summary>
/// Tick is set at runtime. There is no need to manually assign this value.
/// </summary>
private uint _tick;
public void Dispose()
{
OneTimeInputs.ResetState();
}
public uint GetTick() => _tick;
public void SetTick(uint value) => _tick = value;
}
public struct ReconcileData : IReconcileData
{
public ReconcileData(Vector3 position, float verticalVelocity, float stamina, MovingPlatform currentPlatform)
{
Position = position;
VerticalVelocity = verticalVelocity;
Stamina = stamina;
CurrentPlatform = currentPlatform;
_tick = 0;
}
/// <summary>
/// Position of the character.
/// </summary>
public Vector3 Position;
/// <summary>
/// Current vertical velocity.
/// </summary>
/// <remarks>Used to simulate jumps and falls.</remarks>
public float VerticalVelocity;
/// <summary>
/// Amount of stamina remaining to run or jump.
/// </summary>
public float Stamina;
public MovingPlatform CurrentPlatform;
/// <summary>
/// Tick is set at runtime. There is no need to manually assign this value.
/// </summary>
private uint _tick;
public void Dispose() { }
public uint GetTick() => _tick;
public void SetTick(uint value) => _tick = value;
}
#endregion
/// <summary>
/// Invokes whenever NetworkStart is called for owner.
/// </summary>
public static event Action<CharacterControllerPrediction> OnOwner;
/// <summary>
/// Current stamina remaining.
/// </summary>
public float Stamina { get; private set; }
/// <summary>
/// Amount of force for jumping.
/// </summary>
[Tooltip("Amount of force for jumping.")]
[SerializeField]
private float _jumpForce = 30f;
/// <summary>
/// How quickly to move.
/// </summary>
[Tooltip("How quickly to move.")]
[SerializeField]
private float _moveRate = 4f;
/// <summary>
/// Current vertical velocity.
/// </summary>
private float _verticalVelocity;
/// <summary>
/// One-time inputs accumulated over frames between ticks.
/// </summary>
private OneTimeInput _oneTimeInputs = new();
/// <summary>
/// Last data which was supplied during replicate outside of reconcile.
/// </summary>
private ReplicateData _lastTickedReplicateData = default;
/// <summary>
/// Current platform the player is on.
/// </summary>
private MovingPlatform _currentPlatform;
/// <summary>
/// Reference to the CharacterController component.
/// </summary>
private CharacterController _characterController;
/// <summary>
/// NetworkTrigger on this character; used to detact platforms.
/// </summary>
private NetworkTrigger _characterTrigger;
/// <summary>
/// maximum amount of stamina allowed.
/// </summary>
public const float Maximum_Stamina = 50f;
private void Awake()
{
_characterController = GetComponent<CharacterController>();
_characterTrigger = GetComponentInChildren<NetworkTrigger>();
_characterTrigger.OnEnter += CharacterTrigger_OnEnter;
_characterTrigger.OnExit += CharacterTrigger_OnExit;
SetTickCallbacks(TickCallback.Tick | TickCallback.PostTick);
}
public override void OnOwnershipClient(NetworkConnection prevOwner)
{
if (IsOwner)
OnOwner?.Invoke(this);
}
private void Update()
{
SetOneTimeInputs();
}
/// <summary>
/// Checks setting inputs which are one-time(not held).
/// </summary>
private void SetOneTimeInputs()
{
if (!IsOwner)
return;
/* Check to jump. */
if (Input.GetKeyDown(KeyCode.Space))
_oneTimeInputs.Jump = true;
}
protected override void TimeManager_OnTick()
{
PerformReplicate(BuildMoveData());
}
protected override void TimeManager_OnPostTick()
{
/* Typically a CharacterController could send a reconcile within
* OnTick, after completing all moving logic. However, this demo
* uses a NetworkTrigger to jump on platforms, which we need the results
* from before sending a reconcile. The NetworkTrigger/Collider components
* trace after simulation which occurs between OnTick, and OnPostTick, therefor
* the trace would have happened by the time OnPostTick is called. */
CreateReconcile();
}
/// <summary>
/// Returns replicate data to send as the controller.
/// </summary>
private ReplicateData BuildMoveData()
{
/* Only the controller needs to build move data.
* This could be the server if the server if no owner, for example
* such as AI, or the owner of the object. */
if (!IsOwner)
return default;
float horizontal = Input.GetAxisRaw("Horizontal");
float vertical = Input.GetAxisRaw("Vertical");
// Run when left shift is held.
bool run = Input.GetKey(KeyCode.LeftShift);
ReplicateData md = new(new(horizontal, vertical), run, _oneTimeInputs);
// Reset one tine inputs since they've been processed for the tick.
_oneTimeInputs.ResetState();
return md;
}
/// <summary>
/// Creates a reconcile that is sent to clients.
/// </summary>
public override void CreateReconcile()
{
/* Both the server and client should create reconcile data.
* The client will use their copy as a fallback if they do not
* get data from the server, such as a dropped packet.
*
* The client will not reconcile unless it receives at least one
* reconcile packet from the server for the tick. */
/* You do not have to reconcile every tick if you wish to
* save bandwidth/perf, or simply feel as though it's not needed
* for your game type.
*
* Even when not reconciling every tick it's still recommended
* to build the reconcile as client; this cost is very little.*/
/* This is an example of only sending a reconcile occasionally
* if the server. Simply uncomment the if statement below to
* test this behavior. */
// if (base.IsServerStarted)
// {
// // Exit early if 10 ticks have not passed.
// if (base.TimeManager.LocalTick % 10 != 0) return;
// }
// Build the data using current information and call the reconcile method.
ReconcileData rd = new(transform.localPosition, _verticalVelocity, Stamina, _currentPlatform);
PerformReconcile(rd);
}
[Replicate]
private void PerformReplicate(ReplicateData rd, ReplicateState state = ReplicateState.Invalid, Channel channel = Channel.Unreliable)
{
// Always use the tickDelta as your delta when performing actions inside replicate.
float delta = (float)TimeManager.TickDelta;
bool useDefaultForces = false;
/* When client only run some checks to
* further predict the clients future movement.
* This can keep the object more inlined with real-time by
* guessing what the clients input might be before we
* actually receive it.
*
* Doing this does risk a chance of graphical jitter in the
* scenario a de-synchronization occurs, but if only predicting
* a couple ticks the chances are low. */
// See https://fish-networking.gitbook.io/docs/manual/guides/prediction/version-2/creating-code/predicting-states
if (!IsServerStarted && !IsOwner)
{
/* If ticked then set last ticked value.
* Ticked means the replicate is being run from the tick cycle, more
* specifically NOT from a replay/reconcile. */
if (state.ContainsTicked())
{
/* Dispose of old should it have anything that needs to be cleaned up.
* If you are only using value types in your data you do not need to call Dispose.
* You must implement dispose manually to cache any non-value types, if you wish. */
_lastTickedReplicateData.Dispose();
// Set new.
_lastTickedReplicateData = rd;
}
/* In the future means there is no way the data can be known to this client
* yet. For example, the client is running this script locally and due to
* how networking works, they have not yet received the latest information from
* the server.
*
* If in the future then we are only going to predict up to
* a certain amount of ticks in the future. This is us assuming that the
* server (or client which owns this in this case) is going to use the
* same input for at least X number of ticks. You can predict none, or as many
* as you like, but the more inputs you predict the higher likeliness of guessing
* wrong. If you do however predict wrong often smoothing will cover up the mistake. */
else if (state.IsFuture())
{
/* Predict up to 1 tick more. */
if (rd.GetTick() - _lastTickedReplicateData.GetTick() > 1)
{
useDefaultForces = true;
}
else
{
/* If here we are predicting the future. */
/* You likely do not need to dispose rd here since it would be default
* when state is 'not created'. We are simply doing it for good practice, should your ReplicateData
* contain any garbage collection. */
rd.Dispose();
rd = _lastTickedReplicateData;
/* There are some fields you might not want to predict, for example
* jump. The odds of a client pressing jump two ticks in a row is unlikely.
* The stamina check below would likely prevent such a scenario.
*
* We're going to unset jump for this reason. */
rd.OneTimeInputs.Jump = false;
/* Be aware that future predicting is not a one-size fits all
* feature. How much you predict into the future, if at all, depends
* on your game mechanics and your desired outcome. */
}
}
}
Vector3 forces;
if (useDefaultForces)
{
/* Character controllers are a bit problematic with colliders.
* If you were to pass Vector3.zero into the move then there's
* a chance other colliders will clip through the characterController.
* When this is combined with reconciles, it's practically gauranteed
* this will happen.
*
* Because of this issue, if we are 'using default forces' we will apply a
* very insignificant amount of force which makes the characterController
* update properly. */
forces = new(0f, -1f, 0f);
}
// Calculate forces.
else
{
// Stamina regained over every second.
const float regainedStamina = 25f;
// Add stamina with every tick.
ModifyStamina(regainedStamina * delta);
// Add gravity. Extra gravity is added for snappier jumps.
_verticalVelocity += Physics.gravity.y * delta * 3f;
//Cap gravity to -20f so the player doesn't fall too fast.
if (_verticalVelocity < -40f)
_verticalVelocity = -40f;
//Normalize direction so the player does not move faster at angles.
rd.Input = rd.Input.normalized;
/* Typically speaking any modification which can affect your CSP (prediction) should occur
* inside replicate. This is why we add/remove stamina, and move within replicate. */
//Default run multiplier.
float runMultiplier;
//Stamina required to run over a second.
const float runStamina = 50f;
if (rd.Run && TryRemoveStamina(runStamina * delta))
runMultiplier = 1.5f;
else
runMultiplier = 1f;
//Stamina required to jump.
const byte jumpStamina = 30;
/* For consistent jumps set to jump force when jumping, rather
* than add force onto current gravity. */
if (rd.OneTimeInputs.Jump && TryRemoveStamina(jumpStamina))
_verticalVelocity = _jumpForce;
forces = new Vector3(rd.Input.x, 0f, rd.Input.y) * (_moveRate * runMultiplier);
//Add vertical velocity to forces.
forces.y = _verticalVelocity;
}
_characterController.Move(forces * delta);
}
[Reconcile]
private void PerformReconcile(ReconcileData rd, Channel channel = Channel.Unreliable)
{
/* Simply set current values to as they are
* in the reconcile data. */
_verticalVelocity = rd.VerticalVelocity;
Stamina = rd.Stamina;
/* It is VERY important to disable the CharacterController
* component before updating its position. If you do not
* use the disable/enable work-around below, the Transform
* will show the correct position but the physics for
* the CharacterController will remain at it's prior position
* until the next simulate. */
_characterController.enabled = false;
/* Even though the platform is traced for in replicate we must also
* pass the current platform into the reconcile, and set our local value
* to whatever is provided in the reconcile.
*
* This is done because we use local position to reset the character
* and if the clients currentPlatform differs from what the server
* had when reconciling the world position would be significantly
* different due to the parent not aligning. */
_currentPlatform = rd.CurrentPlatform;
//Set transform parent after assigning current.
SetParent();
/* Update position AFTER setting the parent, otherwise
* you would face a potentially huge positional de-sync
* as mentioned above. */
transform.localPosition = rd.Position;
_characterController.enabled = true;
}
/// <summary>
/// Called when the trigger on this object enters another collider.
/// </summary>
private void CharacterTrigger_OnEnter(Collider c)
{
//We only care about moving platforms.
if (!c.TryGetComponent(out MovingPlatform mp))
return;
_currentPlatform = mp;
SetParent();
}
/// <summary>
/// Called when the trigger on this object exits another collider.
/// </summary>
private void CharacterTrigger_OnExit(Collider c)
{
if (c == null)
return;
if (!c.TryGetComponent(out MovingPlatform mp))
return;
//Only check if already attached to a platform.
if (_currentPlatform == null)
return;
//Not the current platform.
if (_currentPlatform != mp)
return;
//Is the current platform, unset.
_currentPlatform = null;
SetParent();
}
/// <summary>
/// Updates parent state based on current platforms value.
/// </summary>
private void SetParent()
{
if (_currentPlatform != null)
transform.SetParent(_currentPlatform.transform);
else
transform.SetParent(null);
}
/// <summary>
/// Modifies stamina by adding or removing stamina.
/// </summary>
private void ModifyStamina(float value)
{
float next = Stamina + value;
Stamina = Mathf.Clamp(next, 0f, Maximum_Stamina);
}
/// <summary>
/// Removes stamina if enough stamina is available.
/// </summary>
/// <returns>>True if stamina was available and removed.</returns>
private bool TryRemoveStamina(float value)
{
if (Stamina < value)
return false;
Stamina -= value;
return true;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 53a6d84aa5d214e48a7308646e5d20da
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Demos/Prediction/CharacterController/Scripts/CharacterControllerPrediction.cs
uploadId: 866910
@@ -0,0 +1,144 @@
using System.Collections.Generic;
using FishNet.Object;
using FishNet.Object.Prediction;
using FishNet.Transporting;
using FishNet.Utility.Template;
using UnityEngine;
namespace FishNet.Demo.Prediction.CharacterControllers
{
public class MovingPlatform : TickNetworkBehaviour
{
#region Types.
public struct ReplicateData : IReplicateData
{
public ReplicateData(uint unused = 0)
{
_tick = 0;
}
/// <summary>
/// Tick is set at runtime. There is no need to manually assign this value.
/// </summary>
private uint _tick;
public void Dispose() { }
public uint GetTick() => _tick;
public void SetTick(uint value) => _tick = value;
}
public struct ReconcileData : IReconcileData
{
public ReconcileData(Vector3 position, byte goalIndex)
{
Position = position;
GoalIndex = goalIndex;
_tick = 0;
}
/// <summary>
/// Position of the character.
/// </summary>
public Vector3 Position;
/// <summary>
/// Current vertical velocity.
/// </summary>
/// <remarks>Used to simulate jumps and falls.</remarks>
public byte GoalIndex;
/// <summary>
/// Tick is set at runtime. There is no need to manually assign this value.
/// </summary>
private uint _tick;
public void Dispose() { }
public uint GetTick() => _tick;
public void SetTick(uint value) => _tick = value;
}
#endregion
[SerializeField]
private float _moveRate = 4f;
/// <summary>
/// Goal to move towards.
/// </summary>
private byte _goalIndex;
/// <summary>
/// Goals to move towards.
/// </summary>
private List<Vector3> _goals = new();
private void Awake()
{
const float offset = 5f;
Vector3 position = transform.position;
_goals.Add(position + new Vector3(0f, 0f, offset));
_goals.Add(position + new Vector3(0f, 0f, -offset));
}
public override void OnStartNetwork()
{
SetTickCallbacks(TickCallback.Tick);
}
protected override void TimeManager_OnTick()
{
PerformReplicate(default);
CreateReconcile();
}
/// <summary>
/// Creates a reconcile that is sent to clients.
/// </summary>
public override void CreateReconcile()
{
ReconcileData rd = new(transform.position, _goalIndex);
PerformReconcile(rd);
}
[Replicate]
private void PerformReplicate(ReplicateData rd, ReplicateState state = ReplicateState.Invalid, Channel channel = Channel.Unreliable)
{
/* Move logic is always called regardless of state.
*
* This means that move will be called when replaying after a reconcile,
* as well during OnTick, as well even if there is no data being received
* from the server (IsFuture).
*
* By doing this we allow the platform to be ahead of what the client has
* received by the server.
*
* When observed the client will actually see the platform
* ahead of the server depending on their ping, being more ahead with higher pings.
* This is the desired outcome as that's where the platform will be by the time
* the client sends input to the server. If the platform was not ahead of the server
* then when the client perhaps jumped onto it, the platform would actually be further
* on the server then where the client observed it. In result, the client would snap to
* a correct position when landing on the platform.
*
* If you want to visually see this simple uncomment the line below. Be sure to add
* latency to make the correction more noticeable. */
// if (state.IsFuture())
// return;
// Always use the tickDelta as your delta when performing actions inside replicate.
float delta = (float)TimeManager.TickDelta;
Vector3 goal = _goals[_goalIndex];
Vector3 next = Vector3.MoveTowards(transform.position, goal, delta * _moveRate);
transform.position = next;
if (next == goal)
{
_goalIndex++;
if (_goalIndex >= _goals.Count)
_goalIndex = 0;
}
}
[Reconcile]
private void PerformReconcile(ReconcileData rd, Channel channel = Channel.Unreliable)
{
transform.position = rd.Position;
_goalIndex = rd.GoalIndex;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 0fca32023f23445429675b0c692d00d1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Demos/Prediction/CharacterController/Scripts/MovingPlatform.cs
uploadId: 866910
@@ -0,0 +1 @@
//Intentionally left blank.
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 2ca42d40476d05444acd0179c78b44cc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Demos/Prediction/CharacterController/Scripts/StaminaCanvas.cs
uploadId: 866910
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d3aa9f1d4a89e9d438bd7a3048417f2f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,154 @@
fileFormatVersion: 2
guid: 26148ffb3c1a62d4cb3446654373058b
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 512
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Demos/Prediction/CharacterController/Textures/AdaptiveInterpolation_Img.png
uploadId: 866910
@@ -0,0 +1,154 @@
fileFormatVersion: 2
guid: e93a25d03197614429e1a8b5600d5a97
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 512
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Demos/Prediction/CharacterController/Textures/FlatInterpolation_Img.png
uploadId: 866910
@@ -0,0 +1,111 @@
fileFormatVersion: 2
guid: f9b2bae1e919cc648a88e211cd094c53
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 1
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Demos/Prediction/CharacterController/Textures/Stamina_Bar.png
uploadId: 866910