WIP: Steam controller support (#221).
* NEW: Add Steamworks.NET to demo. * NEW: Can associate control layouts with actions. * NEW: Can export .inputactions to Steam IGA files. * NEW: Add Steamworks.NET to demo. * NEW: Can associate control layouts with actions. * NEW: Can export .inputactions to Steam IGA files. * NEW: Add Steam IGA version of DemoControls. * NEW: Can generate input devices from Steam IGA files.
This commit is contained in:
Родитель
fe2054b9c2
Коммит
538d2601da
|
@ -0,0 +1,64 @@
|
|||
#if (UNITY_EDITOR || UNITY_STANDALONE) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT
|
||||
using UnityEngine;
|
||||
using UnityEngine.Experimental.Input;
|
||||
using UnityEngine.Experimental.Input.Controls;
|
||||
using UnityEngine.Experimental.Input.Utilities;
|
||||
using UnityEngine.Experimental.Input.Plugins.Steam;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[InitializeOnLoad]
|
||||
#endif
|
||||
[InputControlLayout(stateType = typeof(DemoControllerState))]
|
||||
public class DemoController : SteamController, IInputUpdateCallbackReceiver
|
||||
{
|
||||
private static InputDeviceMatcher deviceMatcher
|
||||
{
|
||||
get { return new InputDeviceMatcher().WithInterface("Steam").WithProduct("DemoController"); }
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
static DemoController()
|
||||
{
|
||||
InputSystem.RegisterControlLayout<DemoController>(matches: deviceMatcher);
|
||||
}
|
||||
|
||||
#endif
|
||||
public void OnUpdate(InputUpdateType updateType)
|
||||
{
|
||||
////TODO
|
||||
}
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(loadType: RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||
private static void RuntimeInitializeOnLoad()
|
||||
{
|
||||
InputSystem.RegisterControlLayout<DemoController>(matches: deviceMatcher);
|
||||
}
|
||||
|
||||
public Vector2Control move { get; protected set; }
|
||||
public Vector2Control look { get; protected set; }
|
||||
public ButtonControl fire { get; protected set; }
|
||||
protected override void FinishSetup(InputDeviceBuilder builder)
|
||||
{
|
||||
base.FinishSetup(builder);
|
||||
move = builder.GetControl<Vector2Control>("move");
|
||||
look = builder.GetControl<Vector2Control>("look");
|
||||
fire = builder.GetControl<ButtonControl>("fire");
|
||||
}
|
||||
}
|
||||
public unsafe struct DemoControllerState : IInputStateTypeInfo
|
||||
{
|
||||
public FourCC GetFormat()
|
||||
{
|
||||
return new FourCC('D', 'e', 'm', 'o');
|
||||
}
|
||||
|
||||
[InputControl(name = "move", layout = "Vector2")]
|
||||
public Vector2 move;
|
||||
[InputControl(name = "look", layout = "Vector2")]
|
||||
public Vector2 look;
|
||||
[InputControl(name = "fire", layout = "Button", bit = 0)]
|
||||
public fixed byte buttons[1];
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b4e398b2b56be40c190845a5001d2276
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1 +1,134 @@
|
|||
{"maps":[{"name":"gameplay","actions":[{"name":"fire","bindings":[]},{"name":"move","bindings":[]},{"name":"look","bindings":[]}],"bindings":[{"name":"","path":"*/{PrimaryAction}","interactions":"Tap,SlowTap","groups":"","action":"fire","chainWithPrevious":false,"isComposite":false,"isPartOfComposite":false,"modifiers":""},{"name":"","path":"<Gamepad>/leftStick","interactions":"","groups":"","action":"move","chainWithPrevious":false,"isComposite":false,"isPartOfComposite":false,"modifiers":""},{"name":"Dpad","path":"Dpad","interactions":"","groups":"","action":"move","chainWithPrevious":false,"isComposite":true,"isPartOfComposite":false,"modifiers":""},{"name":"up","path":"<Keyboard>/w","interactions":"","groups":"","action":"move","chainWithPrevious":false,"isComposite":false,"isPartOfComposite":true,"modifiers":""},{"name":"down","path":"<Keyboard>/s","interactions":"","groups":"","action":"move","chainWithPrevious":false,"isComposite":false,"isPartOfComposite":true,"modifiers":""},{"name":"left","path":"<Keyboard>/a","interactions":"","groups":"","action":"move","chainWithPrevious":false,"isComposite":false,"isPartOfComposite":true,"modifiers":""},{"name":"right","path":"<Keyboard>/d","interactions":"","groups":"","action":"move","chainWithPrevious":false,"isComposite":false,"isPartOfComposite":true,"modifiers":""},{"name":"","path":"<Gamepad>/rightStick","interactions":"","groups":"","action":"look","chainWithPrevious":false,"isComposite":false,"isPartOfComposite":false,"modifiers":""},{"name":"","path":"<Pointer>/delta","interactions":"","groups":"","action":"look","chainWithPrevious":false,"isComposite":false,"isPartOfComposite":false,"modifiers":""}]}]}
|
||||
{
|
||||
"maps": [
|
||||
{
|
||||
"name": "gameplay",
|
||||
"actions": [
|
||||
{
|
||||
"name": "fire",
|
||||
"expectedControlLayout": "Button",
|
||||
"bindings": []
|
||||
},
|
||||
{
|
||||
"name": "move",
|
||||
"expectedControlLayout": "Stick",
|
||||
"bindings": []
|
||||
},
|
||||
{
|
||||
"name": "look",
|
||||
"expectedControlLayout": "Vector2",
|
||||
"bindings": []
|
||||
}
|
||||
],
|
||||
"bindings": [
|
||||
{
|
||||
"name": "",
|
||||
"path": "*/{PrimaryAction}",
|
||||
"interactions": "Tap,SlowTap",
|
||||
"processors": "",
|
||||
"groups": "",
|
||||
"action": "fire",
|
||||
"chainWithPrevious": false,
|
||||
"isComposite": false,
|
||||
"isPartOfComposite": false,
|
||||
"modifiers": ""
|
||||
},
|
||||
{
|
||||
"name": "",
|
||||
"path": "<Gamepad>/leftStick",
|
||||
"interactions": "",
|
||||
"processors": "",
|
||||
"groups": "",
|
||||
"action": "move",
|
||||
"chainWithPrevious": false,
|
||||
"isComposite": false,
|
||||
"isPartOfComposite": false,
|
||||
"modifiers": ""
|
||||
},
|
||||
{
|
||||
"name": "Dpad",
|
||||
"path": "Dpad",
|
||||
"interactions": "",
|
||||
"processors": "",
|
||||
"groups": "",
|
||||
"action": "move",
|
||||
"chainWithPrevious": false,
|
||||
"isComposite": true,
|
||||
"isPartOfComposite": false,
|
||||
"modifiers": ""
|
||||
},
|
||||
{
|
||||
"name": "up",
|
||||
"path": "<Keyboard>/w",
|
||||
"interactions": "",
|
||||
"processors": "",
|
||||
"groups": "",
|
||||
"action": "move",
|
||||
"chainWithPrevious": false,
|
||||
"isComposite": false,
|
||||
"isPartOfComposite": true,
|
||||
"modifiers": ""
|
||||
},
|
||||
{
|
||||
"name": "down",
|
||||
"path": "<Keyboard>/s",
|
||||
"interactions": "",
|
||||
"processors": "",
|
||||
"groups": "",
|
||||
"action": "move",
|
||||
"chainWithPrevious": false,
|
||||
"isComposite": false,
|
||||
"isPartOfComposite": true,
|
||||
"modifiers": ""
|
||||
},
|
||||
{
|
||||
"name": "left",
|
||||
"path": "<Keyboard>/a",
|
||||
"interactions": "",
|
||||
"processors": "",
|
||||
"groups": "",
|
||||
"action": "move",
|
||||
"chainWithPrevious": false,
|
||||
"isComposite": false,
|
||||
"isPartOfComposite": true,
|
||||
"modifiers": ""
|
||||
},
|
||||
{
|
||||
"name": "right",
|
||||
"path": "<Keyboard>/d",
|
||||
"interactions": "",
|
||||
"processors": "",
|
||||
"groups": "",
|
||||
"action": "move",
|
||||
"chainWithPrevious": false,
|
||||
"isComposite": false,
|
||||
"isPartOfComposite": true,
|
||||
"modifiers": ""
|
||||
},
|
||||
{
|
||||
"name": "",
|
||||
"path": "<Gamepad>/rightStick",
|
||||
"interactions": "",
|
||||
"processors": "",
|
||||
"groups": "",
|
||||
"action": "look",
|
||||
"chainWithPrevious": false,
|
||||
"isComposite": false,
|
||||
"isPartOfComposite": false,
|
||||
"modifiers": ""
|
||||
},
|
||||
{
|
||||
"name": "",
|
||||
"path": "<Pointer>/delta",
|
||||
"interactions": "",
|
||||
"processors": "",
|
||||
"groups": "",
|
||||
"action": "look",
|
||||
"chainWithPrevious": false,
|
||||
"isComposite": false,
|
||||
"isPartOfComposite": false,
|
||||
"modifiers": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
"In Game Actions"
|
||||
{
|
||||
"actions"
|
||||
{
|
||||
"gameplay"
|
||||
{
|
||||
"title" "#Set_gameplay"
|
||||
"StickPadGyro"
|
||||
{
|
||||
"move"
|
||||
{
|
||||
"title" "#Action_gameplay_move"
|
||||
"input_mode" "joystick_move"
|
||||
}
|
||||
"look"
|
||||
{
|
||||
"title" "#Action_gameplay_look"
|
||||
"input_mode" "absolute_mouse"
|
||||
}
|
||||
}
|
||||
"AnalogTrigger"
|
||||
{
|
||||
}
|
||||
"Button"
|
||||
{
|
||||
"fire" "Action_gameplay_fire"
|
||||
}
|
||||
}
|
||||
}
|
||||
"localization"
|
||||
{
|
||||
"english"
|
||||
{
|
||||
"Set_gameplay" "gameplay"
|
||||
"Action_gameplay_move" "move"
|
||||
"Action_gameplay_look" "look"
|
||||
"Action_gameplay_fire" "fire"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3e43849d533b147caa9ad95417108e62
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: eb53bdebdf3f542d79f3f6626f274a7c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,279 @@
|
|||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!29 &1
|
||||
OcclusionCullingSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_OcclusionBakeSettings:
|
||||
smallestOccluder: 5
|
||||
smallestHole: 0.25
|
||||
backfaceThreshold: 100
|
||||
m_SceneGUID: 00000000000000000000000000000000
|
||||
m_OcclusionCullingData: {fileID: 0}
|
||||
--- !u!104 &2
|
||||
RenderSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 9
|
||||
m_Fog: 0
|
||||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
||||
m_FogMode: 3
|
||||
m_FogDensity: 0.01
|
||||
m_LinearFogStart: 0
|
||||
m_LinearFogEnd: 300
|
||||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
|
||||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
|
||||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
|
||||
m_AmbientIntensity: 1
|
||||
m_AmbientMode: 0
|
||||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
|
||||
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_HaloStrength: 0.5
|
||||
m_FlareStrength: 1
|
||||
m_FlareFadeSpeed: 3
|
||||
m_HaloTexture: {fileID: 0}
|
||||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_DefaultReflectionMode: 0
|
||||
m_DefaultReflectionResolution: 128
|
||||
m_ReflectionBounces: 1
|
||||
m_ReflectionIntensity: 1
|
||||
m_CustomReflection: {fileID: 0}
|
||||
m_Sun: {fileID: 0}
|
||||
m_IndirectSpecularColor: {r: 0.44657856, g: 0.49641234, b: 0.57481724, a: 1}
|
||||
m_UseRadianceAmbientProbe: 0
|
||||
--- !u!157 &3
|
||||
LightmapSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 11
|
||||
m_GIWorkflowMode: 0
|
||||
m_GISettings:
|
||||
serializedVersion: 2
|
||||
m_BounceScale: 1
|
||||
m_IndirectOutputScale: 1
|
||||
m_AlbedoBoost: 1
|
||||
m_TemporalCoherenceThreshold: 1
|
||||
m_EnvironmentLightingMode: 0
|
||||
m_EnableBakedLightmaps: 1
|
||||
m_EnableRealtimeLightmaps: 1
|
||||
m_LightmapEditorSettings:
|
||||
serializedVersion: 10
|
||||
m_Resolution: 2
|
||||
m_BakeResolution: 40
|
||||
m_AtlasSize: 1024
|
||||
m_AO: 0
|
||||
m_AOMaxDistance: 1
|
||||
m_CompAOExponent: 1
|
||||
m_CompAOExponentDirect: 0
|
||||
m_Padding: 2
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_LightmapsBakeMode: 1
|
||||
m_TextureCompression: 1
|
||||
m_FinalGather: 0
|
||||
m_FinalGatherFiltering: 1
|
||||
m_FinalGatherRayCount: 256
|
||||
m_ReflectionCompression: 2
|
||||
m_MixedBakeMode: 2
|
||||
m_BakeBackend: 1
|
||||
m_PVRSampling: 1
|
||||
m_PVRDirectSampleCount: 32
|
||||
m_PVRSampleCount: 500
|
||||
m_PVRBounces: 2
|
||||
m_PVRFilterTypeDirect: 0
|
||||
m_PVRFilterTypeIndirect: 0
|
||||
m_PVRFilterTypeAO: 0
|
||||
m_PVRFilteringMode: 1
|
||||
m_PVRCulling: 1
|
||||
m_PVRFilteringGaussRadiusDirect: 1
|
||||
m_PVRFilteringGaussRadiusIndirect: 5
|
||||
m_PVRFilteringGaussRadiusAO: 2
|
||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
|
||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2
|
||||
m_PVRFilteringAtrousPositionSigmaAO: 1
|
||||
m_ShowResolutionOverlay: 1
|
||||
m_LightingDataAsset: {fileID: 0}
|
||||
m_UseShadowmask: 1
|
||||
--- !u!196 &4
|
||||
NavMeshSettings:
|
||||
serializedVersion: 2
|
||||
m_ObjectHideFlags: 0
|
||||
m_BuildSettings:
|
||||
serializedVersion: 2
|
||||
agentTypeID: 0
|
||||
agentRadius: 0.5
|
||||
agentHeight: 2
|
||||
agentSlope: 45
|
||||
agentClimb: 0.4
|
||||
ledgeDropHeight: 0
|
||||
maxJumpAcrossDistance: 0
|
||||
minRegionArea: 2
|
||||
manualCellSize: 0
|
||||
cellSize: 0.16666667
|
||||
manualTileSize: 0
|
||||
tileSize: 256
|
||||
accuratePlacement: 0
|
||||
debug:
|
||||
m_Flags: 0
|
||||
m_NavMeshData: {fileID: 0}
|
||||
--- !u!1 &534744310
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 534744313}
|
||||
- component: {fileID: 534744312}
|
||||
- component: {fileID: 534744311}
|
||||
- component: {fileID: 534744314}
|
||||
m_Layer: 0
|
||||
m_Name: Main Camera
|
||||
m_TagString: MainCamera
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!81 &534744311
|
||||
AudioListener:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 534744310}
|
||||
m_Enabled: 1
|
||||
--- !u!20 &534744312
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 534744310}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
m_ClearFlags: 1
|
||||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
|
||||
m_projectionMatrixMode: 1
|
||||
m_SensorSize: {x: 36, y: 24}
|
||||
m_LensShift: {x: 0, y: 0}
|
||||
m_GateFitMode: 2
|
||||
m_FocalLength: 50
|
||||
m_NormalizedViewPortRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
field of view: 60
|
||||
orthographic: 0
|
||||
orthographic size: 5
|
||||
m_Depth: -1
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingPath: -1
|
||||
m_TargetTexture: {fileID: 0}
|
||||
m_TargetDisplay: 0
|
||||
m_TargetEye: 3
|
||||
m_HDR: 1
|
||||
m_AllowMSAA: 1
|
||||
m_AllowDynamicResolution: 0
|
||||
m_ForceIntoRT: 0
|
||||
m_OcclusionCulling: 1
|
||||
m_StereoConvergence: 10
|
||||
m_StereoSeparation: 0.022
|
||||
--- !u!4 &534744313
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 534744310}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 1, z: -10}
|
||||
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 &534744314
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 534744310}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: ef4bffeda13d7a748973ff9204401c07, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!1 &575173927
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 575173929}
|
||||
- component: {fileID: 575173928}
|
||||
m_Layer: 0
|
||||
m_Name: Directional Light
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!108 &575173928
|
||||
Light:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 575173927}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 8
|
||||
m_Type: 1
|
||||
m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
|
||||
m_Intensity: 1
|
||||
m_Range: 10
|
||||
m_SpotAngle: 30
|
||||
m_CookieSize: 10
|
||||
m_Shadows:
|
||||
m_Type: 2
|
||||
m_Resolution: -1
|
||||
m_CustomResolution: -1
|
||||
m_Strength: 1
|
||||
m_Bias: 0.05
|
||||
m_NormalBias: 0.4
|
||||
m_NearPlane: 0.2
|
||||
m_Cookie: {fileID: 0}
|
||||
m_DrawHalo: 0
|
||||
m_Flare: {fileID: 0}
|
||||
m_RenderMode: 0
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_Lightmapping: 4
|
||||
m_LightShadowCasterMode: 0
|
||||
m_AreaSize: {x: 1, y: 1}
|
||||
m_BounceIntensity: 1
|
||||
m_ColorTemperature: 6570
|
||||
m_UseColorTemperature: 0
|
||||
m_ShadowRadius: 0
|
||||
m_ShadowAngle: 0
|
||||
--- !u!4 &575173929
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 575173927}
|
||||
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
|
||||
m_LocalPosition: {x: 0, y: 3, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 1
|
||||
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a5e48e1b2f6494a789eda22444bcbcc7
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7d28372a5c21f48b49a86afae368349b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 34e2320451cf74f17b639e64fa9b35aa
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bfdc0bd207d0a440c8962e62bfe4db51
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,135 @@
|
|||
// This file is provided under The MIT License as part of Steamworks.NET.
|
||||
// Copyright (c) 2013-2018 Riley Labrecque
|
||||
// Please see the included LICENSE.txt for additional information.
|
||||
|
||||
#if UNITY_ANDROID || UNITY_IOS || UNITY_TIZEN || UNITY_TVOS || UNITY_WEBGL || UNITY_WSA || UNITY_PS4 || UNITY_WII || UNITY_XBOXONE || UNITY_SWITCH
|
||||
#define DISABLESTEAMWORKS
|
||||
#endif
|
||||
|
||||
#if !DISABLESTEAMWORKS
|
||||
|
||||
// Add 'DISABLEREDISTCOPY' to your custom platform defines to disable automatic copying!
|
||||
#if UNITY_5_3_OR_NEWER
|
||||
#define DISABLEREDISTCOPY
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Callbacks;
|
||||
using Steamworks;
|
||||
using System.IO;
|
||||
|
||||
public class RedistCopy
|
||||
{
|
||||
[PostProcessBuild]
|
||||
public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject)
|
||||
{
|
||||
string baseDir;
|
||||
string pluginsDir;
|
||||
switch (target)
|
||||
{
|
||||
case BuildTarget.StandaloneWindows:
|
||||
case BuildTarget.StandaloneWindows64:
|
||||
baseDir = Path.Combine(Path.GetDirectoryName(pathToBuiltProject), Path.GetFileNameWithoutExtension(pathToBuiltProject) + "_Data");
|
||||
pluginsDir = Path.Combine(baseDir, "Plugins");
|
||||
break;
|
||||
case BuildTarget.StandaloneLinux:
|
||||
baseDir = Path.Combine(Path.GetDirectoryName(pathToBuiltProject), Path.GetFileNameWithoutExtension(pathToBuiltProject) + "_Data");
|
||||
pluginsDir = Path.Combine(Path.Combine(baseDir, "Plugins"), "x86");
|
||||
break;
|
||||
case BuildTarget.StandaloneLinux64:
|
||||
case BuildTarget.StandaloneLinuxUniversal:
|
||||
baseDir = Path.Combine(Path.GetDirectoryName(pathToBuiltProject), Path.GetFileNameWithoutExtension(pathToBuiltProject) + "_Data");
|
||||
pluginsDir = Path.Combine(Path.Combine(baseDir, "Plugins"), "x86_64");
|
||||
break;
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
case BuildTarget.StandaloneOSX:
|
||||
#else
|
||||
case BuildTarget.StandaloneOSXIntel:
|
||||
case BuildTarget.StandaloneOSXIntel64:
|
||||
case BuildTarget.StandaloneOSXUniversal:
|
||||
#endif
|
||||
baseDir = Path.Combine(Path.GetDirectoryName(pathToBuiltProject), Path.GetFileNameWithoutExtension(pathToBuiltProject) + ".app");
|
||||
baseDir = Path.Combine(baseDir, "Contents");
|
||||
pluginsDir = Path.Combine(baseDir, "Plugins");
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
string[] DebugInfo =
|
||||
{
|
||||
"Steamworks.NET created by Riley Labrecque",
|
||||
"http://steamworks.github.io",
|
||||
"",
|
||||
"Steamworks.NET Version: " + Steamworks.Version.SteamworksNETVersion,
|
||||
"Steamworks SDK Version: " + Steamworks.Version.SteamworksSDKVersion,
|
||||
"Steam API DLL Version: " + Steamworks.Version.SteamAPIDLLVersion,
|
||||
"Steam API DLL Size: " + Steamworks.Version.SteamAPIDLLSize,
|
||||
"Steam API64 DLL Size: " + Steamworks.Version.SteamAPI64DLLSize,
|
||||
""
|
||||
};
|
||||
File.WriteAllLines(Path.Combine(pluginsDir, "Steamworks.NET.txt"), DebugInfo);
|
||||
|
||||
#if !DISABLEREDISTCOPY
|
||||
if (target == BuildTarget.StandaloneWindows64)
|
||||
{
|
||||
CopyFile("steam_api64.dll", "steam_api64.dll", "Assets/Plugins/x86_64", pathToBuiltProject);
|
||||
}
|
||||
else if (target == BuildTarget.StandaloneWindows)
|
||||
{
|
||||
CopyFile("steam_api.dll", "steam_api.dll", "Assets/Plugins/x86", pathToBuiltProject);
|
||||
}
|
||||
|
||||
string controllerCfg = Path.Combine(Application.dataPath, "controller.vdf");
|
||||
if (File.Exists(controllerCfg))
|
||||
{
|
||||
string strFileDest = Path.Combine(baseDir, "controller.vdf");
|
||||
|
||||
File.Copy(controllerCfg, strFileDest);
|
||||
File.SetAttributes(strFileDest, File.GetAttributes(strFileDest) & ~FileAttributes.ReadOnly);
|
||||
|
||||
if (!File.Exists(strFileDest))
|
||||
{
|
||||
Debug.LogWarning("[Steamworks.NET] Could not copy controller.vdf into the built project. File.Copy() Failed. Place controller.vdf from the Steamworks SDK in the output dir manually.");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void CopyFile(string filename, string outputfilename, string pathToFile, string pathToBuiltProject)
|
||||
{
|
||||
string strCWD = Directory.GetCurrentDirectory();
|
||||
string strSource = Path.Combine(Path.Combine(strCWD, pathToFile), filename);
|
||||
string strFileDest = Path.Combine(Path.GetDirectoryName(pathToBuiltProject), outputfilename);
|
||||
|
||||
if (!File.Exists(strSource))
|
||||
{
|
||||
Debug.LogWarning(string.Format("[Steamworks.NET] Could not copy {0} into the project root. {0} could not be found in '{1}'. Place {0} from the redist into the project root manually.", filename, pathToFile));
|
||||
return;
|
||||
}
|
||||
|
||||
if (File.Exists(strFileDest))
|
||||
{
|
||||
if (File.GetLastWriteTime(strSource) == File.GetLastWriteTime(strFileDest))
|
||||
{
|
||||
FileInfo fInfo = new FileInfo(strSource);
|
||||
FileInfo fInfo2 = new FileInfo(strFileDest);
|
||||
if (fInfo.Length == fInfo2.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File.Copy(strSource, strFileDest, true);
|
||||
File.SetAttributes(strFileDest, File.GetAttributes(strFileDest) & ~FileAttributes.ReadOnly);
|
||||
|
||||
if (!File.Exists(strFileDest))
|
||||
{
|
||||
Debug.LogWarning(string.Format("[Steamworks.NET] Could not copy {0} into the built project. File.Copy() Failed. Place {0} from the redist folder into the output dir manually.", filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !DISABLESTEAMWORKS
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 53d09de5fac8f4a9aae30822cccf007c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,298 @@
|
|||
// This file is provided under The MIT License as part of Steamworks.NET.
|
||||
// Copyright (c) 2013-2018 Riley Labrecque
|
||||
// Please see the included LICENSE.txt for additional information.
|
||||
|
||||
// Uncomment this out or add it to your custom platform defines to disable checking the plugin platform settings.
|
||||
//#define DISABLEPLATFORMSETTINGS
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.IO;
|
||||
|
||||
// This copys various files into their required locations when Unity is launched to make installation a breeze.
|
||||
[InitializeOnLoad]
|
||||
public class RedistInstall
|
||||
{
|
||||
static RedistInstall()
|
||||
{
|
||||
CopyFile("Assets/Plugins/Steamworks.NET/redist", "steam_appid.txt", false);
|
||||
|
||||
// We only need to copy the dll into the project root on <= Unity 5.0
|
||||
#if UNITY_EDITOR_WIN && (UNITY_4_7 || UNITY_5_0)
|
||||
#if UNITY_EDITOR_64
|
||||
CopyFile("Assets/Plugins/x86_64", "steam_api64.dll", true);
|
||||
#else
|
||||
CopyFile("Assets/Plugins/x86", "steam_api.dll", true);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if UNITY_5 || UNITY_2017
|
||||
#if !DISABLEPLATFORMSETTINGS
|
||||
SetPlatformSettings();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static void CopyFile(string path, string filename, bool bCheckDifference)
|
||||
{
|
||||
string strCWD = Directory.GetCurrentDirectory();
|
||||
string strSource = Path.Combine(Path.Combine(strCWD, path), filename);
|
||||
string strDest = Path.Combine(strCWD, filename);
|
||||
|
||||
if (!File.Exists(strSource))
|
||||
{
|
||||
Debug.LogWarning(string.Format("[Steamworks.NET] Could not copy {0} into the project root. {0} could not be found in '{1}'. Place {0} from the Steamworks SDK in the project root manually.", filename, Path.Combine(strCWD, path)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (File.Exists(strDest))
|
||||
{
|
||||
if (!bCheckDifference)
|
||||
return;
|
||||
|
||||
if (File.GetLastWriteTime(strSource) == File.GetLastWriteTime(strDest))
|
||||
{
|
||||
FileInfo fInfo = new FileInfo(strSource);
|
||||
FileInfo fInfo2 = new FileInfo(strDest);
|
||||
if (fInfo.Length == fInfo2.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log(string.Format("[Steamworks.NET] {0} in the project root differs from the Steamworks.NET redistributable. Updating.... Please relaunch Unity.", filename));
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log(string.Format("[Steamworks.NET] {0} is not present in the project root. Copying...", filename));
|
||||
}
|
||||
|
||||
File.Copy(strSource, strDest, true);
|
||||
File.SetAttributes(strDest, File.GetAttributes(strDest) & ~FileAttributes.ReadOnly);
|
||||
|
||||
if (File.Exists(strDest))
|
||||
{
|
||||
Debug.Log(string.Format("[Steamworks.NET] Successfully copied {0} into the project root. Please relaunch Unity.", filename));
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning(string.Format("[Steamworks.NET] Could not copy {0} into the project root. File.Copy() Failed. Please copy {0} into the project root manually.", Path.Combine(path, filename)));
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_5 || UNITY_2017 || UNITY_2017_1_OR_NEWER
|
||||
static void SetPlatformSettings()
|
||||
{
|
||||
foreach (var plugin in PluginImporter.GetAllImporters())
|
||||
{
|
||||
// Skip any null plugins, why is this a thing?!
|
||||
if (plugin == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip any absolute paths, as they are only builtin plugins.
|
||||
if (Path.IsPathRooted(plugin.assetPath))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool didUpdate = false;
|
||||
string filename = Path.GetFileName(plugin.assetPath);
|
||||
|
||||
switch (filename)
|
||||
{
|
||||
case "libsteam_api.dylib":
|
||||
didUpdate |= ResetPluginSettings(plugin, "AnyCPU", "OSX");
|
||||
didUpdate |= SetCompatibleWithOSX(plugin);
|
||||
break;
|
||||
case "libsteam_api.so":
|
||||
if (plugin.assetPath.Contains("x86_64"))
|
||||
{
|
||||
didUpdate |= ResetPluginSettings(plugin, "x86_64", "Linux");
|
||||
didUpdate |= SetCompatibleWithLinux(plugin, BuildTarget.StandaloneLinux64);
|
||||
}
|
||||
else
|
||||
{
|
||||
didUpdate |= ResetPluginSettings(plugin, "x86", "Linux");
|
||||
didUpdate |= SetCompatibleWithLinux(plugin, BuildTarget.StandaloneLinux);
|
||||
}
|
||||
break;
|
||||
case "steam_api.dll":
|
||||
case "steam_api64.dll":
|
||||
if (plugin.assetPath.Contains("x86_64"))
|
||||
{
|
||||
didUpdate |= ResetPluginSettings(plugin, "x86_64", "Windows");
|
||||
#if UNITY_5_3_OR_NEWER
|
||||
didUpdate |= SetCompatibleWithWindows(plugin, BuildTarget.StandaloneWindows64);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
didUpdate |= ResetPluginSettings(plugin, "x86", "Windows");
|
||||
#if UNITY_5_3_OR_NEWER
|
||||
didUpdate |= SetCompatibleWithWindows(plugin, BuildTarget.StandaloneWindows);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !UNITY_5_3_OR_NEWER
|
||||
// We do this because Unity had a bug where dependent dll's didn't get loaded from the Plugins
|
||||
// folder in actual builds. But they do in the editor now! So close... Unity bug number: 728945
|
||||
// So ultimately we must keep using RedistCopy to copy steam_api[64].dll next to the .exe on builds, and
|
||||
// we don't want a useless duplicate version of the dll ending up in the Plugins folder.
|
||||
// This was fixed in Unity 5.3!
|
||||
didUpdate |= SetCompatibleWithEditor(plugin);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
if (didUpdate)
|
||||
{
|
||||
plugin.SaveAndReimport();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool ResetPluginSettings(PluginImporter plugin, string CPU, string OS)
|
||||
{
|
||||
bool didUpdate = false;
|
||||
|
||||
if (plugin.GetCompatibleWithAnyPlatform() != false)
|
||||
{
|
||||
plugin.SetCompatibleWithAnyPlatform(false);
|
||||
didUpdate = true;
|
||||
}
|
||||
|
||||
if (plugin.GetCompatibleWithEditor() != true)
|
||||
{
|
||||
plugin.SetCompatibleWithEditor(true);
|
||||
didUpdate = true;
|
||||
}
|
||||
|
||||
if (plugin.GetEditorData("CPU") != CPU)
|
||||
{
|
||||
plugin.SetEditorData("CPU", CPU);
|
||||
didUpdate = true;
|
||||
}
|
||||
|
||||
if (plugin.GetEditorData("OS") != OS)
|
||||
{
|
||||
plugin.SetEditorData("OS", OS);
|
||||
didUpdate = true;
|
||||
}
|
||||
|
||||
return didUpdate;
|
||||
}
|
||||
|
||||
static bool SetCompatibleWithOSX(PluginImporter plugin)
|
||||
{
|
||||
bool didUpdate = false;
|
||||
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneOSX, true);
|
||||
#else
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneOSXIntel, true);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneOSXIntel64, true);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneOSXUniversal, true);
|
||||
#endif
|
||||
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneLinux, false);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneLinux64, false);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneLinuxUniversal, false);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneWindows, false);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneWindows64, false);
|
||||
|
||||
return didUpdate;
|
||||
}
|
||||
|
||||
static bool SetCompatibleWithLinux(PluginImporter plugin, BuildTarget platform)
|
||||
{
|
||||
bool didUpdate = false;
|
||||
|
||||
if (platform == BuildTarget.StandaloneLinux)
|
||||
{
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneLinux, true);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneLinux64, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneLinux, false);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneLinux64, true);
|
||||
}
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneLinuxUniversal, true);
|
||||
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneOSX, false);
|
||||
#else
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneOSXIntel, false);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneOSXIntel64, false);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneOSXUniversal, false);
|
||||
#endif
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneWindows, false);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneWindows64, false);
|
||||
|
||||
return didUpdate;
|
||||
}
|
||||
|
||||
static bool SetCompatibleWithWindows(PluginImporter plugin, BuildTarget platform)
|
||||
{
|
||||
bool didUpdate = false;
|
||||
|
||||
if (platform == BuildTarget.StandaloneWindows)
|
||||
{
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneWindows, true);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneWindows64, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneWindows, false);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneWindows64, true);
|
||||
}
|
||||
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneLinux64, false);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneLinux, false);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneLinuxUniversal, false);
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneOSX, false);
|
||||
#else
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneOSXIntel, false);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneOSXIntel64, false);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneOSXUniversal, false);
|
||||
#endif
|
||||
|
||||
return didUpdate;
|
||||
}
|
||||
|
||||
static bool SetCompatibleWithEditor(PluginImporter plugin)
|
||||
{
|
||||
bool didUpdate = false;
|
||||
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneLinux64, false);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneLinux, false);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneLinuxUniversal, false);
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneOSX, false);
|
||||
#else
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneOSXIntel, false);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneOSXIntel64, false);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneOSXUniversal, false);
|
||||
#endif
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneWindows, false);
|
||||
didUpdate |= SetCompatibleWithPlatform(plugin, BuildTarget.StandaloneWindows64, false);
|
||||
|
||||
return didUpdate;
|
||||
}
|
||||
|
||||
static bool SetCompatibleWithPlatform(PluginImporter plugin, BuildTarget platform, bool enable)
|
||||
{
|
||||
if (plugin.GetCompatibleWithPlatform(platform) == enable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
plugin.SetCompatibleWithPlatform(platform, enable);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // UNITY_5 || UNITY_2017 || UNITY_2017_1_OR_NEWER
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0900d89463138441bb65f51e556baabe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-2018 Riley Labrecque
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 58d2228fde8c747dbb016e0cf7e340c4
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,32 @@
|
|||
Steamworks.NET
|
||||
=======
|
||||
|
||||
_Steamworks.NET_ is a C# Wrapper for Valve's Steamworks API, it can be used either with Unity or your C# based Application.
|
||||
|
||||
_Steamworks.NET_ was designed to be as close as possible to the original C++ API, as such the documentation provided from Valve largely covers usage of _Steamworks.NET_.
|
||||
Niceties and C# Idioms can be easily implemented on top of _Steamworks.NET_.
|
||||
|
||||
_Steamworks.NET_ currently fully supports Windows, OSX, and Linux in both 32 and 64bit varieties. Currently building against Steamworks SDK 1.42.
|
||||
|
||||
* Author: [Riley Labrecque](https://github.com/rlabrecque)
|
||||
* License: [MIT](http://www.opensource.org/licenses/mit-license.php)
|
||||
* [Documentation](https://steamworks.github.io/)
|
||||
* [Discussion Thread](http://steamcommunity.com/groups/steamworks/discussions/0/666827974770212954/)
|
||||
* [Reporting Issues](https://github.com/rlabrecque/Steamworks.NET/issues)
|
||||
* 1-on-1 support is available by donating $100 USD or greater.
|
||||
* Support can be obtained via [Email](mailto:support@rileylabrecque.com), [Skype](http://rileylabrecque.com/skype), or [Steam](http://steamcommunity.com/id/rlabrecque)
|
||||
* I can only help with Steamworks.NET specific issues, general API questions should be asked on the [Steamworks discussion board](http://steamcommunity.com/groups/steamworks/discussions).
|
||||
|
||||
[![Support via Paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YFZZER8VNXKRC)
|
||||
|
||||
|
||||
[Installation Instructions](http://steamworks.github.io/installation/)
|
||||
-----
|
||||
|
||||
Samples
|
||||
-----
|
||||
Check out these sample projects to get started:
|
||||
* [Steamworks.NET Example](https://github.com/rlabrecque/Steamworks.NET-Example)
|
||||
* [Steamworks.NET Test](https://github.com/rlabrecque/Steamworks.NET-Test)
|
||||
* [Steamworks.NET ChatClient](https://github.com/rlabrecque/Steamworks.NET-ChatClient)
|
||||
* [Steamworks.NET GameServerTest](https://github.com/rlabrecque/Steamworks.NET-GameServerTest)
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 008d7012a0f2449fcaced391169a881f
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,178 @@
|
|||
// The SteamManager is designed to work with Steamworks.NET
|
||||
// This file is released into the public domain.
|
||||
// Where that dedication is not recognized you are granted a perpetual,
|
||||
// irrevokable license to copy and modify this file as you see fit.
|
||||
//
|
||||
// Version: 1.0.5
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using Steamworks;
|
||||
|
||||
//
|
||||
// The SteamManager provides a base implementation of Steamworks.NET on which you can build upon.
|
||||
// It handles the basics of starting up and shutting down the SteamAPI for use.
|
||||
//
|
||||
[DisallowMultipleComponent]
|
||||
public class SteamManager : MonoBehaviour
|
||||
{
|
||||
private static SteamManager s_instance;
|
||||
private static SteamManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_instance == null)
|
||||
{
|
||||
return new GameObject("SteamManager").AddComponent<SteamManager>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return s_instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool s_EverInialized;
|
||||
|
||||
private bool m_bInitialized;
|
||||
public static bool Initialized
|
||||
{
|
||||
get
|
||||
{
|
||||
return Instance.m_bInitialized;
|
||||
}
|
||||
}
|
||||
|
||||
private SteamAPIWarningMessageHook_t m_SteamAPIWarningMessageHook;
|
||||
private static void SteamAPIDebugTextHook(int nSeverity, System.Text.StringBuilder pchDebugText)
|
||||
{
|
||||
Debug.LogWarning(pchDebugText);
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Only one instance of SteamManager at a time!
|
||||
if (s_instance != null)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
s_instance = this;
|
||||
|
||||
if (s_EverInialized)
|
||||
{
|
||||
// This is almost always an error.
|
||||
// The most common case where this happens is when SteamManager gets destroyed because of Application.Quit(),
|
||||
// and then some Steamworks code in some other OnDestroy gets called afterwards, creating a new SteamManager.
|
||||
// You should never call Steamworks functions in OnDestroy, always prefer OnDisable if possible.
|
||||
throw new System.Exception("Tried to Initialize the SteamAPI twice in one session!");
|
||||
}
|
||||
|
||||
// We want our SteamManager Instance to persist across scenes.
|
||||
DontDestroyOnLoad(gameObject);
|
||||
|
||||
if (!Packsize.Test())
|
||||
{
|
||||
Debug.LogError("[Steamworks.NET] Packsize Test returned false, the wrong version of Steamworks.NET is being run in this platform.", this);
|
||||
}
|
||||
|
||||
if (!DllCheck.Test())
|
||||
{
|
||||
Debug.LogError("[Steamworks.NET] DllCheck Test returned false, One or more of the Steamworks binaries seems to be the wrong version.", this);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// If Steam is not running or the game wasn't started through Steam, SteamAPI_RestartAppIfNecessary starts the
|
||||
// Steam client and also launches this game again if the User owns it. This can act as a rudimentary form of DRM.
|
||||
|
||||
// Once you get a Steam AppID assigned by Valve, you need to replace AppId_t.Invalid with it and
|
||||
// remove steam_appid.txt from the game depot. eg: "(AppId_t)480" or "new AppId_t(480)".
|
||||
// See the Valve documentation for more information: https://partner.steamgames.com/doc/sdk/api#initialization_and_shutdown
|
||||
if (SteamAPI.RestartAppIfNecessary(AppId_t.Invalid))
|
||||
{
|
||||
Application.Quit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (System.DllNotFoundException e) // We catch this exception here, as it will be the first occurence of it.
|
||||
{
|
||||
Debug.LogError("[Steamworks.NET] Could not load [lib]steam_api.dll/so/dylib. It's likely not in the correct location. Refer to the README for more details.\n" + e, this);
|
||||
|
||||
Application.Quit();
|
||||
return;
|
||||
}
|
||||
|
||||
// Initializes the Steamworks API.
|
||||
// If this returns false then this indicates one of the following conditions:
|
||||
// [*] The Steam client isn't running. A running Steam client is required to provide implementations of the various Steamworks interfaces.
|
||||
// [*] The Steam client couldn't determine the App ID of game. If you're running your application from the executable or debugger directly then you must have a [code-inline]steam_appid.txt[/code-inline] in your game directory next to the executable, with your app ID in it and nothing else. Steam will look for this file in the current working directory. If you are running your executable from a different directory you may need to relocate the [code-inline]steam_appid.txt[/code-inline] file.
|
||||
// [*] Your application is not running under the same OS user context as the Steam client, such as a different user or administration access level.
|
||||
// [*] Ensure that you own a license for the App ID on the currently active Steam account. Your game must show up in your Steam library.
|
||||
// [*] Your App ID is not completely set up, i.e. in [code-inline]Release State: Unavailable[/code-inline], or it's missing default packages.
|
||||
// Valve's documentation for this is located here:
|
||||
// https://partner.steamgames.com/doc/sdk/api#initialization_and_shutdown
|
||||
m_bInitialized = SteamAPI.Init();
|
||||
if (!m_bInitialized)
|
||||
{
|
||||
Debug.LogError("[Steamworks.NET] SteamAPI_Init() failed. Refer to Valve's documentation or the comment above this line for more information.", this);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
s_EverInialized = true;
|
||||
}
|
||||
|
||||
// This should only ever get called on first load and after an Assembly reload, You should never Disable the Steamworks Manager yourself.
|
||||
private void OnEnable()
|
||||
{
|
||||
if (s_instance == null)
|
||||
{
|
||||
s_instance = this;
|
||||
}
|
||||
|
||||
if (!m_bInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_SteamAPIWarningMessageHook == null)
|
||||
{
|
||||
// Set up our callback to recieve warning messages from Steam.
|
||||
// You must launch with "-debug_steamapi" in the launch args to recieve warnings.
|
||||
m_SteamAPIWarningMessageHook = new SteamAPIWarningMessageHook_t(SteamAPIDebugTextHook);
|
||||
SteamClient.SetWarningMessageHook(m_SteamAPIWarningMessageHook);
|
||||
}
|
||||
}
|
||||
|
||||
// OnApplicationQuit gets called too early to shutdown the SteamAPI.
|
||||
// Because the SteamManager should be persistent and never disabled or destroyed we can shutdown the SteamAPI here.
|
||||
// Thus it is not recommended to perform any Steamworks work in other OnDestroy functions as the order of execution can not be garenteed upon Shutdown. Prefer OnDisable().
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (s_instance != this)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
s_instance = null;
|
||||
|
||||
if (!m_bInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SteamAPI.Shutdown();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!m_bInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Run Steam client callbacks
|
||||
SteamAPI.RunCallbacks();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ef4bffeda13d7a748973ff9204401c07
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1 @@
|
|||
480
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 03a6b6df3b6824d6eb29597e4ab12575
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -100,6 +100,22 @@ namespace UnityEngine.Experimental.Input
|
|||
get { return m_Name; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name of control layout expected for controls bound to this action.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is optional and is null by default.
|
||||
///
|
||||
/// Constraining an action to a particular control layout allows determine the value
|
||||
/// type and expected input behavior of an action without being reliant on any particular
|
||||
/// binding.
|
||||
/// </remarks>
|
||||
public string expectedControlLayout
|
||||
{
|
||||
get { return m_ExpectedControlLayout; }
|
||||
set { m_ExpectedControlLayout = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The map the action belongs to.
|
||||
/// </summary>
|
||||
|
@ -347,8 +363,9 @@ namespace UnityEngine.Experimental.Input
|
|||
return Clone();
|
||||
}
|
||||
|
||||
////REVIEW: for binding resolution, it would be best if this was an InternedString; however, for serialization, it has to be a string
|
||||
////REVIEW: it would be best if these were InternedStrings; however, for serialization, it has to be strings
|
||||
[SerializeField] internal string m_Name;
|
||||
[SerializeField] internal string m_ExpectedControlLayout;
|
||||
|
||||
// For singleton actions, we serialize the bindings directly as part of the action.
|
||||
// For any other type of action, this is null.
|
||||
|
|
|
@ -638,6 +638,7 @@ namespace UnityEngine.Experimental.Input
|
|||
private struct ActionJson
|
||||
{
|
||||
public string name;
|
||||
public string expectedControlLayout;
|
||||
|
||||
// Bindings can either be on the action itself (in which case the action name
|
||||
// for each binding is implied) or listed separately in the action file.
|
||||
|
@ -646,7 +647,11 @@ namespace UnityEngine.Experimental.Input
|
|||
public static ActionJson FromAction(InputAction action)
|
||||
{
|
||||
// Bindings don't go on the actions when we write them.
|
||||
return new ActionJson {name = action.m_Name};
|
||||
return new ActionJson
|
||||
{
|
||||
name = action.m_Name,
|
||||
expectedControlLayout = action.m_ExpectedControlLayout,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -787,6 +792,9 @@ namespace UnityEngine.Experimental.Input
|
|||
|
||||
// Create action.
|
||||
var action = new InputAction(actionName);
|
||||
action.m_ExpectedControlLayout = !string.IsNullOrEmpty(jsonAction.expectedControlLayout)
|
||||
? jsonAction.expectedControlLayout
|
||||
: null;
|
||||
actionLists[mapIndex].Add(action);
|
||||
|
||||
// Add bindings.
|
||||
|
@ -846,6 +854,9 @@ namespace UnityEngine.Experimental.Input
|
|||
|
||||
// Create action.
|
||||
var action = new InputAction(jsonAction.name);
|
||||
action.m_ExpectedControlLayout = !string.IsNullOrEmpty(jsonAction.expectedControlLayout)
|
||||
? jsonAction.expectedControlLayout
|
||||
: null;
|
||||
actionLists[mapIndex].Add(action);
|
||||
|
||||
// Add bindings.
|
||||
|
|
|
@ -213,7 +213,8 @@ namespace UnityEngine.Experimental.Input
|
|||
}
|
||||
}
|
||||
|
||||
public static InputAction AddAction(this InputActionMap map, string name, string binding = null, string interactions = null, string groups = null)
|
||||
public static InputAction AddAction(this InputActionMap map, string name, string binding = null,
|
||||
string interactions = null, string groups = null, string expectedControlLayout = null)
|
||||
{
|
||||
if (map == null)
|
||||
throw new ArgumentNullException("map");
|
||||
|
@ -228,6 +229,7 @@ namespace UnityEngine.Experimental.Input
|
|||
|
||||
// Append action to array.
|
||||
var action = new InputAction(name);
|
||||
action.expectedControlLayout = expectedControlLayout;
|
||||
ArrayHelpers.Append(ref map.m_Actions, action);
|
||||
action.m_ActionMap = map;
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@ namespace UnityEngine.Experimental.Input.Editor
|
|||
|
||||
public static InputControlLayout TryGetLayout(string name)
|
||||
{
|
||||
Refresh();
|
||||
return s_Cache.FindOrLoadLayout(name);
|
||||
}
|
||||
|
||||
|
|
|
@ -40,8 +40,10 @@ namespace UnityEngine.Experimental.Input.Editor
|
|||
: base(state)
|
||||
{
|
||||
m_ApplyAction = applyAction;
|
||||
//FIXME: this requires 2018.3 to compile
|
||||
//foldoutOverride += OnFoldoutDraw;
|
||||
////REVIEW: good enough like this for 2018.2?
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
foldoutOverride += OnFoldoutDraw;
|
||||
#endif
|
||||
Reload();
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ using UnityEditor;
|
|||
using UnityEditorInternal;
|
||||
using UnityEngine.Experimental.Input.Utilities;
|
||||
|
||||
////REVIEW: "properties" seems wrong; these seems to revert to "parameters" specifically
|
||||
|
||||
namespace UnityEngine.Experimental.Input.Editor.Lists
|
||||
{
|
||||
class InteractionsReorderableReorderableList : PropertiesReorderableList
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0332615c34a2b464680d0b6435a22738
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -264,8 +264,8 @@ namespace UnityEngine.Experimental.Input.Editor
|
|||
|
||||
var id = 1;
|
||||
var usages = BuildTreeForUsages(ref id);
|
||||
var devices = AddChild(root, "Devices", ref id);
|
||||
var products = AddChild(root, "Products", ref id);
|
||||
var devices = AddChild(root, "Abstract Devices", ref id);
|
||||
var products = AddChild(root, "Specific Devices", ref id);
|
||||
|
||||
foreach (var layout in EditorInputControlLayoutCache.allDeviceLayouts)
|
||||
{
|
||||
|
|
|
@ -1282,7 +1282,7 @@ namespace UnityEngine.Experimental.Input
|
|||
#endif
|
||||
|
||||
// Send an initial Update so that user methods such as Start and Awake
|
||||
// can access the input devices prior to thier Upate methods.
|
||||
// can access the input devices prior to their Update methods.
|
||||
Update();
|
||||
}
|
||||
|
||||
|
@ -1322,6 +1322,10 @@ namespace UnityEngine.Experimental.Input
|
|||
#if UNITY_EDITOR || UNITY_ANDROID || UNITY_IOS || UNITY_TVOS || UNITY_WSA
|
||||
Plugins.OnScreen.OnScreenSupport.Initialize();
|
||||
#endif
|
||||
|
||||
#if (UNITY_EDITOR || UNITY_STANDALONE) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT
|
||||
Plugins.Steam.SteamSupport.Initialize();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#if (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT
|
||||
|
||||
namespace UnityEngine.Experimental.Input.Plugins.Steam
|
||||
{
|
||||
public interface IStreamControllerAPI
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#endif // (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 425bde5d5c63e4c5d8d95b068a911236
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,3 +1,5 @@
|
|||
#if (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT
|
||||
|
||||
namespace UnityEngine.Experimental.Input.Plugins.Steam
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -5,7 +7,7 @@ namespace UnityEngine.Experimental.Input.Plugins.Steam
|
|||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Unlike other controllers, the Steam controller is somewhat of an amorphic input
|
||||
/// device which gains specfic shape only in combination with VDF files. These files
|
||||
/// device which gains specific shape only in combination with VDF files. These files
|
||||
/// specify the actions that are supported by the application and are internally bound
|
||||
/// to specific controls on a controller inside the Steam runtime.
|
||||
///
|
||||
|
@ -15,7 +17,8 @@ namespace UnityEngine.Experimental.Input.Plugins.Steam
|
|||
/// </remarks>
|
||||
public class SteamController : InputDevice
|
||||
{
|
||||
public const string kSteamInterface = "Steam";
|
||||
}
|
||||
|
||||
//add support for constructing InputControlLayouts from Steam VDF files
|
||||
}
|
||||
|
||||
#endif // (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
////TODO: write a ScriptedImporter for VDF files which automatically generates a layout
|
||||
#if (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT
|
||||
|
||||
namespace UnityEngine.Experimental.Input.Plugins.Steam
|
||||
{
|
||||
|
@ -11,3 +11,5 @@ namespace UnityEngine.Experimental.Input.Plugins.Steam
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
namespace UnityEngine.Experimental.Input.Plugins.Steam
|
||||
{
|
||||
public class SteamTests
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e66d8e0ae15b4ae5ab2d917c90cd824b
|
||||
timeCreated: 1511133961
|
|
@ -1,10 +1,641 @@
|
|||
////REVIEW: What we really want, I think, is a *layout* in the system that represents the device
|
||||
//// with the actions being controls. Exporting InputActions to .VDF seems to not make much sense
|
||||
#if UNITY_EDITOR && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
using UnityEngine.Experimental.Input.Controls;
|
||||
using UnityEngine.Experimental.Input.Editor;
|
||||
using UnityEngine.Experimental.Input.Utilities;
|
||||
|
||||
namespace UnityEngine.Experimental.Input.Plugins.Steam
|
||||
////TODO: also need to build a layout based on SteamController that has controls representing the current set of actions
|
||||
//// (might need this in the runtime)
|
||||
|
||||
////TODO: localization support (allow loading existing VDF file and preserving localization strings)
|
||||
|
||||
////TODO: allow having actions that are ignored by Steam VDF export
|
||||
|
||||
////TODO: support for getting displayNames/glyphs from Steam
|
||||
|
||||
////TODO: polling in background
|
||||
|
||||
namespace UnityEngine.Experimental.Input.Plugins.Steam.Editor
|
||||
{
|
||||
// Exports an .inputactions asset into Steam .VDF format.
|
||||
public class SteamVDFExporter
|
||||
/// <summary>
|
||||
/// Converts input actions to and from Steam IGA file format.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The idea behind this converter is to enable users to use Unity's action editor to set up actions
|
||||
/// for their game and the be able, when targeting desktops through Steam, to convert the game's actions
|
||||
/// to a Steam VDF file that allows using the Steam Controller API with the game.
|
||||
///
|
||||
/// The generated VDF file is meant to allow editing by hand in order to add localization strings or
|
||||
/// apply Steam-specific settings that cannot be inferred from Unity input actions.
|
||||
/// </remarks>
|
||||
public static class SteamIGAConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Generate C# code for an <see cref="InputDevice"/> derived class that exposes the controls
|
||||
/// for the actions found in the given Steam IGA description.
|
||||
/// </summary>
|
||||
/// <param name="vdf"></param>
|
||||
/// <param name="namespaceAndClassName"></param>
|
||||
/// <returns></returns>
|
||||
public static string GenerateInputDeviceFromSteamIGA(string vdf, string namespaceAndClassName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(vdf))
|
||||
throw new ArgumentNullException("vdf");
|
||||
if (string.IsNullOrEmpty(namespaceAndClassName))
|
||||
throw new ArgumentNullException("namespaceAndClassName");
|
||||
|
||||
// Parse VDF.
|
||||
var parsedVdf = ParseVDF(vdf);
|
||||
var actions = (Dictionary<string, object>)((Dictionary<string, object>)parsedVdf["In Game Actions"])["actions"];
|
||||
|
||||
// Determine class and namespace name.
|
||||
var namespaceName = "";
|
||||
var className = "";
|
||||
var indexOfLastDot = namespaceAndClassName.LastIndexOf('.');
|
||||
if (indexOfLastDot != -1)
|
||||
{
|
||||
namespaceName = namespaceAndClassName.Substring(0, indexOfLastDot);
|
||||
className = namespaceAndClassName.Substring(indexOfLastDot + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
className = namespaceAndClassName;
|
||||
}
|
||||
var stateStructName = className + "State";
|
||||
|
||||
var builder = new StringBuilder();
|
||||
|
||||
builder.Append("#if (UNITY_EDITOR || UNITY_STANDALONE) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT\n");
|
||||
builder.Append("using UnityEngine;\n");
|
||||
builder.Append("using UnityEngine.Experimental.Input;\n");
|
||||
builder.Append("using UnityEngine.Experimental.Input.Controls;\n");
|
||||
builder.Append("using UnityEngine.Experimental.Input.Utilities;\n");
|
||||
builder.Append("using UnityEngine.Experimental.Input.Plugins.Steam;\n");
|
||||
builder.Append("#if UNITY_EDITOR\n");
|
||||
builder.Append("using UnityEditor;\n");
|
||||
builder.Append("#endif\n");
|
||||
builder.Append("\n");
|
||||
if (!string.IsNullOrEmpty(namespaceName))
|
||||
{
|
||||
builder.Append("namespace ");
|
||||
builder.Append(namespaceName);
|
||||
builder.Append("\n{\n");
|
||||
}
|
||||
|
||||
// InitializeOnLoad attribute.
|
||||
builder.Append("#if UNITY_EDITOR\n");
|
||||
builder.Append("[InitializeOnLoad]\n");
|
||||
builder.Append("#endif\n");
|
||||
|
||||
// Control layout attribute.
|
||||
builder.Append("[InputControlLayout(stateType = typeof(");
|
||||
builder.Append(stateStructName);
|
||||
builder.Append("))]\n");
|
||||
|
||||
// Class declaration.
|
||||
builder.Append("public class ");
|
||||
builder.Append(className);
|
||||
builder.Append(" : SteamController, IInputUpdateCallbackReceiver\n{\n");
|
||||
|
||||
// Device matcher.
|
||||
builder.Append(" private static InputDeviceMatcher deviceMatcher\n");
|
||||
builder.Append(" {\n");
|
||||
builder.Append(" get { return new InputDeviceMatcher().WithInterface(\"Steam\").WithProduct(\"");
|
||||
builder.Append(className);
|
||||
builder.Append("\"); }\n");
|
||||
builder.Append(" }\n");
|
||||
|
||||
// Static constructor.
|
||||
builder.Append("#if UNITY_EDITOR\n");
|
||||
builder.Append(" static ");
|
||||
builder.Append(className);
|
||||
builder.Append("()\n");
|
||||
builder.Append(" {\n");
|
||||
builder.Append(" InputSystem.RegisterControlLayout<");
|
||||
builder.Append(className);
|
||||
builder.Append(">(matches: deviceMatcher);\n");
|
||||
builder.Append(" }\n");
|
||||
builder.Append("#endif\n");
|
||||
|
||||
// Update method.
|
||||
builder.Append(" public void OnUpdate(InputUpdateType updateType)\n");
|
||||
builder.Append(" {\n");
|
||||
builder.Append(" ////TODO\n");
|
||||
builder.Append(" }\n");
|
||||
|
||||
// RuntimeInitializeOnLoadMethod.
|
||||
// NOTE: Not relying on static ctor here. See il2cpp bug 1014293.
|
||||
builder.Append(" [RuntimeInitializeOnLoadMethod(loadType: RuntimeInitializeLoadType.BeforeSceneLoad)]\n");
|
||||
builder.Append(" private static void RuntimeInitializeOnLoad()\n");
|
||||
builder.Append(" {\n");
|
||||
builder.Append(" InputSystem.RegisterControlLayout<");
|
||||
builder.Append(className);
|
||||
builder.Append(">(matches: deviceMatcher);\n");
|
||||
builder.Append(" }\n");
|
||||
|
||||
// Control properties.
|
||||
foreach (var setEntry in actions)
|
||||
{
|
||||
var setEntryProperties = (Dictionary<string, object>)setEntry.Value;
|
||||
|
||||
// StickPadGyros.
|
||||
var stickPadGyros = (Dictionary<string, object>)setEntryProperties["StickPadGyro"];
|
||||
foreach (var entry in stickPadGyros)
|
||||
{
|
||||
var entryProperties = (Dictionary<string, object>)entry.Value;
|
||||
var isStick = entryProperties.ContainsKey("input_mode") && entryProperties["input_mode"] == "joystick_move";
|
||||
builder.Append(string.Format(" public {0} {1} {{ get; protected set; }}\n", isStick ? "StickControl" : "Vector2Control",
|
||||
CSharpCodeHelpers.MakeIdentifier(entry.Key)));
|
||||
}
|
||||
|
||||
// Buttons.
|
||||
var buttons = (Dictionary<string, object>)setEntryProperties["Button"];
|
||||
foreach (var entry in buttons)
|
||||
{
|
||||
builder.Append(string.Format(" public ButtonControl {0} {{ get; protected set; }}\n",
|
||||
CSharpCodeHelpers.MakeIdentifier(entry.Key)));
|
||||
}
|
||||
|
||||
// AnalogTriggers.
|
||||
var analogTriggers = (Dictionary<string, object>)setEntryProperties["AnalogTrigger"];
|
||||
foreach (var entry in analogTriggers)
|
||||
{
|
||||
builder.Append(string.Format(" public AxisControl {0} {{ get; protected set; }}\n",
|
||||
CSharpCodeHelpers.MakeIdentifier(entry.Key)));
|
||||
}
|
||||
}
|
||||
|
||||
// FinishSetup method.
|
||||
builder.Append(" protected override void FinishSetup(InputDeviceBuilder builder)\n");
|
||||
builder.Append(" {\n");
|
||||
builder.Append(" base.FinishSetup(builder);\n");
|
||||
foreach (var setEntry in actions)
|
||||
{
|
||||
var setEntryProperties = (Dictionary<string, object>)setEntry.Value;
|
||||
|
||||
// StickPadGyros.
|
||||
var stickPadGyros = (Dictionary<string, object>)setEntryProperties["StickPadGyro"];
|
||||
foreach (var entry in stickPadGyros)
|
||||
{
|
||||
var entryProperties = (Dictionary<string, object>)entry.Value;
|
||||
var isStick = entryProperties.ContainsKey("input_mode") && entryProperties["input_mode"] == "joystick_move";
|
||||
builder.Append(string.Format(" {0} = builder.GetControl<{1}>(\"{2}\");\n",
|
||||
CSharpCodeHelpers.MakeIdentifier(entry.Key),
|
||||
isStick ? "StickControl" : "Vector2Control",
|
||||
entry.Key));
|
||||
}
|
||||
|
||||
// Buttons.
|
||||
var buttons = (Dictionary<string, object>)setEntryProperties["Button"];
|
||||
foreach (var entry in buttons)
|
||||
{
|
||||
builder.Append(string.Format(" {0} = builder.GetControl<ButtonControl>(\"{1}\");\n",
|
||||
CSharpCodeHelpers.MakeIdentifier(entry.Key),
|
||||
entry.Key));
|
||||
}
|
||||
|
||||
// AnalogTriggers.
|
||||
var analogTriggers = (Dictionary<string, object>)setEntryProperties["AnalogTrigger"];
|
||||
foreach (var entry in analogTriggers)
|
||||
{
|
||||
builder.Append(string.Format(" {0} = builder.GetControl<AxisControl>(\"{1}\");\n",
|
||||
CSharpCodeHelpers.MakeIdentifier(entry.Key),
|
||||
entry.Key));
|
||||
}
|
||||
}
|
||||
builder.Append(" }\n");
|
||||
|
||||
builder.Append("}\n");
|
||||
|
||||
if (!string.IsNullOrEmpty(namespaceName))
|
||||
builder.Append("}\n");
|
||||
|
||||
// State struct.
|
||||
builder.Append("public unsafe struct ");
|
||||
builder.Append(stateStructName);
|
||||
builder.Append(" : IInputStateTypeInfo\n");
|
||||
builder.Append("{\n");
|
||||
builder.Append(" public FourCC GetFormat()\n");
|
||||
builder.Append(" {\n");
|
||||
////TODO: handle class names that are shorter than 4 characters
|
||||
builder.Append(string.Format(" return new FourCC('{0}', '{1}', '{2}', '{3}');\n", className[0], className[1], className[2], className[3]));
|
||||
builder.Append(" }\n");
|
||||
builder.Append("\n");
|
||||
foreach (var setEntry in actions)
|
||||
{
|
||||
var setEntryProperties = (Dictionary<string, object>)setEntry.Value;
|
||||
|
||||
// StickPadGyros.
|
||||
var stickPadGyros = (Dictionary<string, object>)setEntryProperties["StickPadGyro"];
|
||||
foreach (var entry in stickPadGyros)
|
||||
{
|
||||
var entryProperties = (Dictionary<string, object>)entry.Value;
|
||||
var isStick = entryProperties.ContainsKey("input_mode") && entryProperties["input_mode"] == "joystick_move";
|
||||
|
||||
builder.Append(string.Format(" [InputControl(name = \"{0}\", layout = \"{1}\")]\n", entry.Key, isStick ? "Stick" : "Vector2"));
|
||||
builder.Append(string.Format(" public Vector2 {0};\n", CSharpCodeHelpers.MakeIdentifier(entry.Key)));
|
||||
}
|
||||
|
||||
// Buttons.
|
||||
var buttons = (Dictionary<string, object>)setEntryProperties["Button"];
|
||||
var buttonCount = buttons.Count;
|
||||
if (buttonCount > 0)
|
||||
{
|
||||
var bit = 0;
|
||||
foreach (var entry in buttons)
|
||||
{
|
||||
builder.Append(string.Format(
|
||||
" [InputControl(name = \"{0}\", layout = \"Button\", bit = {1})]\n", entry.Key, bit));
|
||||
++bit;
|
||||
}
|
||||
|
||||
var byteCount = (buttonCount + 7) / 8;
|
||||
builder.Append(" public fixed byte buttons[");
|
||||
builder.Append(byteCount.ToString());
|
||||
builder.Append("];\n");
|
||||
}
|
||||
|
||||
// AnalogTriggers.
|
||||
var analogTriggers = (Dictionary<string, object>)setEntryProperties["AnalogTrigger"];
|
||||
foreach (var entry in analogTriggers)
|
||||
{
|
||||
builder.Append(string.Format(" [InputControl(name = \"{0}\", layout = \"Axis\")]\n", entry.Key));
|
||||
builder.Append(string.Format(" public float {0};\n", CSharpCodeHelpers.MakeIdentifier(entry.Key)));
|
||||
}
|
||||
}
|
||||
builder.Append("}\n");
|
||||
|
||||
builder.Append("#endif\n");
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert an .inputactions asset to Steam VDF format.
|
||||
/// </summary>
|
||||
/// <param name="asset"></param>
|
||||
/// <param name="locale"></param>
|
||||
/// <returns>A string in Steam VDF format describing "In Game Actions" corresponding to the actions in
|
||||
/// <paramref name="asset"/>.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="asset"/> is null.</exception>
|
||||
public static string ConvertInputActionsToSteamIGA(InputActionAsset asset, string locale = "english")
|
||||
{
|
||||
if (asset == null)
|
||||
throw new ArgumentNullException("asset");
|
||||
return ConvertInputActionsToSteamIGA(asset.actionMaps, locale: locale);
|
||||
}
|
||||
|
||||
public static string ConvertInputActionsToSteamIGA(IEnumerable<InputActionMap> actionMaps, string locale = "english")
|
||||
{
|
||||
if (actionMaps == null)
|
||||
throw new ArgumentNullException("actionMaps");
|
||||
|
||||
var localizationStrings = new Dictionary<string, string>();
|
||||
|
||||
var builder = new StringBuilder();
|
||||
builder.Append("\"In Game Actions\"\n");
|
||||
builder.Append("{\n");
|
||||
|
||||
// Add actions.
|
||||
builder.Append("\t\"actions\"\n");
|
||||
builder.Append("\t{\n");
|
||||
|
||||
// Add each action map.
|
||||
foreach (var actionMap in actionMaps)
|
||||
{
|
||||
var actionMapName = actionMap.name;
|
||||
var actionMapIdentifier = CSharpCodeHelpers.MakeIdentifier(actionMapName);
|
||||
|
||||
builder.Append("\t\t\"");
|
||||
builder.Append(actionMapName);
|
||||
builder.Append("\"\n");
|
||||
builder.Append("\t\t{\n");
|
||||
|
||||
// Title.
|
||||
builder.Append("\t\t\t\"title\"\t\"#Set_");
|
||||
builder.Append(actionMapIdentifier);
|
||||
builder.Append("\"\n");
|
||||
localizationStrings["Set_" + actionMapIdentifier] = actionMapName;
|
||||
|
||||
// StickPadGyro actions.
|
||||
builder.Append("\t\t\t\"StickPadGyro\"\n");
|
||||
builder.Append("\t\t\t{\n");
|
||||
foreach (var action in actionMap.actions.Where(x => GetSteamControllerInputType(x) == "StickPadGyro"))
|
||||
ConvertInputActionToVDF(action, builder, localizationStrings);
|
||||
builder.Append("\t\t\t}\n");
|
||||
|
||||
// AnalogTrigger actions.
|
||||
builder.Append("\t\t\t\"AnalogTrigger\"\n");
|
||||
builder.Append("\t\t\t{\n");
|
||||
foreach (var action in actionMap.actions.Where(x => GetSteamControllerInputType(x) == "AnalogTrigger"))
|
||||
ConvertInputActionToVDF(action, builder, localizationStrings);
|
||||
builder.Append("\t\t\t}\n");
|
||||
|
||||
// Button actions.
|
||||
builder.Append("\t\t\t\"Button\"\n");
|
||||
builder.Append("\t\t\t{\n");
|
||||
foreach (var action in actionMap.actions.Where(x => GetSteamControllerInputType(x) == "Button"))
|
||||
ConvertInputActionToVDF(action, builder, localizationStrings);
|
||||
builder.Append("\t\t\t}\n");
|
||||
|
||||
builder.Append("\t\t}\n");
|
||||
}
|
||||
|
||||
builder.Append("\t}\n");
|
||||
|
||||
// Add localizations.
|
||||
builder.Append("\t\"localization\"\n");
|
||||
builder.Append("\t{\n");
|
||||
builder.Append("\t\t\"");
|
||||
builder.Append(locale);
|
||||
builder.Append("\"\n");
|
||||
builder.Append("\t\t{\n");
|
||||
foreach (var entry in localizationStrings)
|
||||
{
|
||||
builder.Append("\t\t\t\"");
|
||||
builder.Append(entry.Key);
|
||||
builder.Append("\"\t\"");
|
||||
builder.Append(entry.Value);
|
||||
builder.Append("\"\n");
|
||||
}
|
||||
builder.Append("\t\t}\n");
|
||||
builder.Append("\t}\n");
|
||||
|
||||
builder.Append("}\n");
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static void ConvertInputActionToVDF(InputAction action, StringBuilder builder, Dictionary<string, string> localizationStrings)
|
||||
{
|
||||
builder.Append("\t\t\t\t\"");
|
||||
builder.Append(action.name);
|
||||
|
||||
var mapIdentifier = CSharpCodeHelpers.MakeIdentifier(action.actionMap.name);
|
||||
var actionIdentifier = CSharpCodeHelpers.MakeIdentifier(action.name);
|
||||
var titleId = "Action_" + mapIdentifier + "_" + actionIdentifier;
|
||||
localizationStrings[titleId] = action.name;
|
||||
|
||||
// StickPadGyros are objects. Everything else is just strings.
|
||||
var inputType = GetSteamControllerInputType(action);
|
||||
if (inputType == "StickPadGyro")
|
||||
{
|
||||
builder.Append("\"\n");
|
||||
builder.Append("\t\t\t\t{\n");
|
||||
|
||||
// Title.
|
||||
builder.Append("\t\t\t\t\t\"title\"\t\"#");
|
||||
builder.Append(titleId);
|
||||
builder.Append("\"\n");
|
||||
|
||||
// Decide on "input_mode". Assume "absolute_mouse" by default and take
|
||||
// anything built on StickControl as "joystick_move".
|
||||
var inputMode = "absolute_mouse";
|
||||
var controlType = EditorInputControlLayoutCache.TryGetLayout(action.expectedControlLayout).type;
|
||||
if (typeof(StickControl).IsAssignableFrom(controlType))
|
||||
inputMode = "joystick_move";
|
||||
builder.Append("\t\t\t\t\t\"input_mode\"\t\"");
|
||||
builder.Append(inputMode);
|
||||
builder.Append("\"\n");
|
||||
|
||||
builder.Append("\t\t\t\t}\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append("\"\t\"");
|
||||
builder.Append(titleId);
|
||||
builder.Append("\"\n");
|
||||
}
|
||||
}
|
||||
|
||||
public static InputActionMap[] ConvertInputActionsFromVDF(string vdf)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static string GetSteamControllerInputType(InputAction action)
|
||||
{
|
||||
if (action == null)
|
||||
throw new ArgumentNullException("action");
|
||||
|
||||
// Make sure we have an expected control layout.
|
||||
var expectedControlLayout = action.expectedControlLayout;
|
||||
if (string.IsNullOrEmpty(expectedControlLayout))
|
||||
throw new Exception(string.Format(
|
||||
"Cannot determine Steam input type for action '{0}' that has no associated expected control layout",
|
||||
action));
|
||||
|
||||
// Try to fetch the layout.
|
||||
var layout = EditorInputControlLayoutCache.TryGetLayout(expectedControlLayout);
|
||||
if (layout == null)
|
||||
throw new Exception(string.Format(
|
||||
"Cannot determine Steam input type for action '{0}'; cannot find layout '{1}'", action,
|
||||
expectedControlLayout));
|
||||
|
||||
// Map our supported control types.
|
||||
var controlType = layout.type;
|
||||
if (typeof(ButtonControl).IsAssignableFrom(controlType))
|
||||
return "Button";
|
||||
if (typeof(InputControl<float>).IsAssignableFrom(controlType))
|
||||
return "AnalogTrigger";
|
||||
if (typeof(Vector2Control).IsAssignableFrom(controlType))
|
||||
return "StickPadGyro";
|
||||
|
||||
// Everything else throws.
|
||||
throw new Exception(string.Format(
|
||||
"Cannot determine Steam input type for action '{0}'; layout '{1}' with control type '{2}' has no known representation in the Steam controller API",
|
||||
action, expectedControlLayout, controlType.Name));
|
||||
}
|
||||
|
||||
public static Dictionary<string, object> ParseVDF(string vdf)
|
||||
{
|
||||
var parser = new VDFParser(vdf);
|
||||
return parser.Parse();
|
||||
}
|
||||
|
||||
private struct VDFParser
|
||||
{
|
||||
public string vdf;
|
||||
public int length;
|
||||
public int position;
|
||||
|
||||
public VDFParser(string vdf)
|
||||
{
|
||||
this.vdf = vdf;
|
||||
length = vdf.Length;
|
||||
position = 0;
|
||||
}
|
||||
|
||||
public Dictionary<string, object> Parse()
|
||||
{
|
||||
var result = new Dictionary<string, object>();
|
||||
ParseKeyValuePair(result);
|
||||
SkipWhitespace();
|
||||
if (position < length)
|
||||
throw new Exception(string.Format("Parse error at {0} in '{1}'; not expecting any more input", position, vdf));
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool ParseKeyValuePair(Dictionary<string, object> result)
|
||||
{
|
||||
var key = ParseString();
|
||||
if (key.isEmpty)
|
||||
return false;
|
||||
|
||||
SkipWhitespace();
|
||||
if (position == length)
|
||||
throw new Exception(string.Format("Expecting value or object at position {0} in '{1}'",
|
||||
position, vdf));
|
||||
|
||||
var nextChar = vdf[position];
|
||||
if (nextChar == '"')
|
||||
{
|
||||
var value = ParseString();
|
||||
result[key.ToString()] = value.ToString();
|
||||
}
|
||||
else if (nextChar == '{')
|
||||
{
|
||||
var value = ParseObject();
|
||||
result[key.ToString()] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(string.Format("Expecting value or object at position {0} in '{1}'",
|
||||
position, vdf));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Substring ParseString()
|
||||
{
|
||||
SkipWhitespace();
|
||||
if (position == length || vdf[position] != '"')
|
||||
return new Substring();
|
||||
|
||||
++position;
|
||||
var startPos = position;
|
||||
while (position < length && vdf[position] != '"')
|
||||
++position;
|
||||
var endPos = position;
|
||||
|
||||
if (position < length)
|
||||
++position;
|
||||
|
||||
return new Substring(vdf, startPos, endPos - startPos);
|
||||
}
|
||||
|
||||
private Dictionary<string, object> ParseObject()
|
||||
{
|
||||
SkipWhitespace();
|
||||
if (position == length || vdf[position] != '{')
|
||||
return null;
|
||||
|
||||
var result = new Dictionary<string, object>();
|
||||
|
||||
++position;
|
||||
while (position < length)
|
||||
{
|
||||
if (!ParseKeyValuePair(result))
|
||||
break;
|
||||
}
|
||||
|
||||
SkipWhitespace();
|
||||
if (position == length || vdf[position] != '}')
|
||||
throw new Exception(string.Format("Expecting '}}' at position {0} in '{1}'", position, vdf));
|
||||
++position;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void SkipWhitespace()
|
||||
{
|
||||
while (position < length && char.IsWhiteSpace(vdf[position]))
|
||||
++position;
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Export to Steam In-Game Actions File...", true)]
|
||||
private static bool IsExportContextMenuItemEnabled()
|
||||
{
|
||||
return Selection.activeObject is InputActionAsset;
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Export to Steam In-Game Actions File...")]
|
||||
private static void ExportContextMenuItem()
|
||||
{
|
||||
var selectedAsset = (InputActionAsset)Selection.activeObject;
|
||||
|
||||
// Determine default .vdf file name.
|
||||
var defaultVDFName = "";
|
||||
var directory = "";
|
||||
var assetPath = AssetDatabase.GetAssetPath(selectedAsset);
|
||||
if (!string.IsNullOrEmpty(assetPath))
|
||||
{
|
||||
defaultVDFName = Path.GetFileNameWithoutExtension(assetPath) + ".vdf";
|
||||
directory = Path.GetDirectoryName(assetPath);
|
||||
}
|
||||
|
||||
// Ask for save location.
|
||||
var fileName = EditorUtility.SaveFilePanel("Export Steam In-Game Actions File", directory, defaultVDFName, "vdf");
|
||||
if (!string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
var text = ConvertInputActionsToSteamIGA(selectedAsset);
|
||||
File.WriteAllText(fileName, text);
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Generate C# Input Device Class...", true)]
|
||||
private static bool IsGenerateContextMenuItemEnabled()
|
||||
{
|
||||
// VDF files have no associated importer and so come in as DefaultAssets.
|
||||
if (!(Selection.activeObject is DefaultAsset))
|
||||
return false;
|
||||
|
||||
var assetPath = AssetDatabase.GetAssetPath(Selection.activeObject);
|
||||
if (!string.IsNullOrEmpty(assetPath) && Path.GetExtension(assetPath) == ".vdf")
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////TODO: support setting class and namespace name
|
||||
[MenuItem("Assets/Generate C# Input Device Class...")]
|
||||
private static void GenerateContextMenuItem()
|
||||
{
|
||||
var selectedAsset = Selection.activeObject;
|
||||
var assetPath = AssetDatabase.GetAssetPath(selectedAsset);
|
||||
if (string.IsNullOrEmpty(assetPath))
|
||||
{
|
||||
Debug.LogError("Cannot determine source asset path");
|
||||
return;
|
||||
}
|
||||
|
||||
var defaultClassName = Path.GetFileNameWithoutExtension(assetPath);
|
||||
var defaultFileName = defaultClassName + ".cs";
|
||||
var defaultDirectory = Path.GetDirectoryName(assetPath);
|
||||
|
||||
// Ask for save location.
|
||||
var fileName = EditorUtility.SaveFilePanel("Generate C# Input Device Class", defaultDirectory, defaultFileName, "cs");
|
||||
if (string.IsNullOrEmpty(fileName))
|
||||
return;
|
||||
|
||||
// Load VDF file text.
|
||||
var vdf = File.ReadAllText(assetPath);
|
||||
|
||||
// Generate and write output.
|
||||
var className = Path.GetFileNameWithoutExtension(fileName);
|
||||
var text = GenerateInputDeviceFromSteamIGA(vdf, className);
|
||||
File.WriteAllText(fileName, text);
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UNITY_EDITOR && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#if (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAMWORKSNET_STEAM_CONTRLLER_SUPPORT
|
||||
|
||||
namespace UnityEngine.Experimental.Input.Plugins.Steam
|
||||
{
|
||||
public class SteamworksNETControllerAPI
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#endif // (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAMWORKSNET_STEAM_CONTROLLER_SUPPORT
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4c4583dbeb0e84e0ab6ef74b8aa8ad86
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -681,7 +681,7 @@ partial class CoreTests
|
|||
{
|
||||
var map = new InputActionMap("test");
|
||||
|
||||
map.AddAction(name: "action1", binding: "/gamepad/leftStick")
|
||||
map.AddAction(name: "action1", expectedControlLayout: "Button", binding: "/gamepad/leftStick")
|
||||
.AppendBinding("/gamepad/rightStick")
|
||||
.WithGroup("group")
|
||||
.WithProcessor("deadzone");
|
||||
|
@ -695,6 +695,8 @@ partial class CoreTests
|
|||
Assert.That(maps[0].actions, Has.Count.EqualTo(2));
|
||||
Assert.That(maps[0].actions[0].name, Is.EqualTo("action1"));
|
||||
Assert.That(maps[0].actions[1].name, Is.EqualTo("action2"));
|
||||
Assert.That(maps[0].actions[0].expectedControlLayout, Is.EqualTo("Button"));
|
||||
Assert.That(maps[0].actions[1].expectedControlLayout, Is.Null);
|
||||
Assert.That(maps[0].actions[0].bindings, Has.Count.EqualTo(2));
|
||||
Assert.That(maps[0].actions[1].bindings, Has.Count.EqualTo(1));
|
||||
Assert.That(maps[0].actions[0].bindings[0].groups, Is.Null);
|
||||
|
@ -2445,6 +2447,22 @@ partial class CoreTests
|
|||
InputSystem.Update();
|
||||
}
|
||||
|
||||
// It's possible to associate a control layout name with an action. This is useful both for
|
||||
// characterizing the expected input behavior as well as to make control picking (both at
|
||||
// edit time and in the game) easier.
|
||||
[Test]
|
||||
[Category("Actions")]
|
||||
public void Actions_CanHaveExpectedControlLayout()
|
||||
{
|
||||
var action = new InputAction();
|
||||
|
||||
Assert.That(action.expectedControlLayout, Is.Null);
|
||||
|
||||
action.expectedControlLayout = "Button";
|
||||
|
||||
Assert.That(action.expectedControlLayout, Is.EqualTo("Button"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("Actions")]
|
||||
public void TODO_Actions_HaveStableIDs()
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
#if (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Experimental.Input;
|
||||
using UnityEngine.Experimental.Input.Plugins.Steam.Editor;
|
||||
|
||||
public class SteamTests : InputTestFixture
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
|
||||
[Test]
|
||||
[Category("Editor")]
|
||||
public void Editor_CanGenerateInputDeviceBasedOnSteamIGAFile()
|
||||
{
|
||||
// Create an InputActions setup and convert it to Steam IGA.
|
||||
var asset = ScriptableObject.CreateInstance<InputActionAsset>();
|
||||
var actionMap1 = new InputActionMap("map1");
|
||||
var actionMap2 = new InputActionMap("map2");
|
||||
actionMap1.AddAction("buttonAction", expectedControlLayout: "Button");
|
||||
actionMap1.AddAction("axisAction", expectedControlLayout: "Axis");
|
||||
actionMap1.AddAction("stickAction", expectedControlLayout: "Stick");
|
||||
actionMap2.AddAction("vector2Action", expectedControlLayout: "Vector2");
|
||||
|
||||
asset.AddActionMap(actionMap1);
|
||||
asset.AddActionMap(actionMap2);
|
||||
|
||||
var vdf = SteamIGAConverter.ConvertInputActionsToSteamIGA(asset);
|
||||
|
||||
// Generate a C# input device from the Steam IGA file.
|
||||
var generatedCode = SteamIGAConverter.GenerateInputDeviceFromSteamIGA(vdf, "My.Namespace.MySteamController");
|
||||
|
||||
Assert.That(generatedCode, Contains.Substring("#if (UNITY_EDITOR || UNITY_STANDALONE) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT"));
|
||||
Assert.That(generatedCode, Contains.Substring("namespace My.Namespace\n"));
|
||||
Assert.That(generatedCode, Contains.Substring("public class MySteamController : SteamController, IInputUpdateCallbackReceiver\n"));
|
||||
Assert.That(generatedCode, Contains.Substring("public unsafe struct MySteamControllerState : IInputStateTypeInfo\n"));
|
||||
Assert.That(generatedCode, Contains.Substring("[InitializeOnLoad]"));
|
||||
Assert.That(generatedCode, Contains.Substring("[RuntimeInitializeOnLoadMethod"));
|
||||
Assert.That(generatedCode, Contains.Substring("new FourCC('M', 'y', 'S', 't')"));
|
||||
Assert.That(generatedCode, Contains.Substring("protected override void FinishSetup(InputDeviceBuilder builder)"));
|
||||
Assert.That(generatedCode, Contains.Substring("base.FinishSetup(builder);"));
|
||||
Assert.That(generatedCode, Contains.Substring("new InputDeviceMatcher"));
|
||||
Assert.That(generatedCode, Contains.Substring("WithInterface(\"Steam\")"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("Editor")]
|
||||
public void Editor_CanConvertInputActionsToSteamIGAFormat()
|
||||
{
|
||||
var asset = ScriptableObject.CreateInstance<InputActionAsset>();
|
||||
var actionMap1 = new InputActionMap("map1");
|
||||
var actionMap2 = new InputActionMap("map2");
|
||||
actionMap1.AddAction("buttonAction", expectedControlLayout: "Button");
|
||||
actionMap1.AddAction("axisAction", expectedControlLayout: "Axis");
|
||||
actionMap1.AddAction("stickAction", expectedControlLayout: "Stick");
|
||||
actionMap2.AddAction("vector2Action", expectedControlLayout: "Vector2");
|
||||
|
||||
asset.AddActionMap(actionMap1);
|
||||
asset.AddActionMap(actionMap2);
|
||||
|
||||
var vdf = SteamIGAConverter.ConvertInputActionsToSteamIGA(asset);
|
||||
var dictionary = SteamIGAConverter.ParseVDF(vdf);
|
||||
|
||||
// Top-level key "In Game Actions".
|
||||
Assert.That(dictionary.Count, Is.EqualTo(1));
|
||||
Assert.That(dictionary, Contains.Key("In Game Actions").With.TypeOf<Dictionary<string, object>>());
|
||||
|
||||
// "actions" and "localization" inside "In Game Actions".
|
||||
var inGameActions = (Dictionary<string, object>)dictionary["In Game Actions"];
|
||||
Assert.That(inGameActions, Contains.Key("actions"));
|
||||
Assert.That(inGameActions["actions"], Is.TypeOf<Dictionary<string, object>>());
|
||||
Assert.That(inGameActions, Contains.Key("localization"));
|
||||
Assert.That(inGameActions["localization"], Is.TypeOf<Dictionary<string, object>>());
|
||||
Assert.That(inGameActions.Count, Is.EqualTo(2));
|
||||
|
||||
// Two action maps inside "actions".
|
||||
var actions = (Dictionary<string, object>)inGameActions["actions"];
|
||||
Assert.That(actions, Contains.Key("map1"));
|
||||
Assert.That(actions["map1"], Is.TypeOf<Dictionary<string, object>>());
|
||||
Assert.That(actions, Contains.Key("map2"));
|
||||
Assert.That(actions["map2"], Is.TypeOf<Dictionary<string, object>>());
|
||||
Assert.That(actions.Count, Is.EqualTo(2));
|
||||
|
||||
// Three actions inside "map1".
|
||||
var map1 = (Dictionary<string, object>)actions["map1"];
|
||||
Assert.That(map1, Contains.Key("title"));
|
||||
Assert.That(map1, Contains.Key("StickPadGyro"));
|
||||
Assert.That(map1, Contains.Key("AnalogTrigger"));
|
||||
Assert.That(map1, Contains.Key("Button"));
|
||||
Assert.That(map1.Count, Is.EqualTo(4));
|
||||
Assert.That(map1["title"], Is.EqualTo("#Set_map1"));
|
||||
Assert.That(map1["StickPadGyro"], Is.TypeOf<Dictionary<string, object>>());
|
||||
Assert.That(map1["AnalogTrigger"], Is.TypeOf<Dictionary<string, object>>());
|
||||
Assert.That(map1["Button"], Is.TypeOf<Dictionary<string, object>>());
|
||||
var stickPadGyro1 = (Dictionary<string, object>)map1["StickPadGyro"];
|
||||
Assert.That(stickPadGyro1, Has.Count.EqualTo(1));
|
||||
Assert.That(stickPadGyro1, Contains.Key("stickAction"));
|
||||
Assert.That(stickPadGyro1["stickAction"], Is.TypeOf<Dictionary<string, object>>());
|
||||
var stickAction = (Dictionary<string, object>)stickPadGyro1["stickAction"];
|
||||
Assert.That(stickAction, Contains.Key("title"));
|
||||
Assert.That(stickAction.Count, Is.EqualTo(2));
|
||||
Assert.That(stickAction["title"], Is.EqualTo("#Action_map1_stickAction"));
|
||||
|
||||
// One action inside "map2".
|
||||
var map2 = (Dictionary<string, object>)actions["map2"];
|
||||
Assert.That(map2, Contains.Key("title"));
|
||||
Assert.That(map2["title"], Is.EqualTo("#Set_map2"));
|
||||
|
||||
// Localization strings.
|
||||
var localization = (Dictionary<string, object>)inGameActions["localization"];
|
||||
Assert.That(localization.Count, Is.EqualTo(1));
|
||||
Assert.That(localization, Contains.Key("english"));
|
||||
Assert.That(localization["english"], Is.TypeOf<Dictionary<string, object>>());
|
||||
var english = (Dictionary<string, object>)localization["english"];
|
||||
Assert.That(english, Contains.Key("Set_map1"));
|
||||
Assert.That(english, Contains.Key("Set_map2"));
|
||||
Assert.That(english, Contains.Key("Action_map1_buttonAction"));
|
||||
Assert.That(english, Contains.Key("Action_map1_axisAction"));
|
||||
Assert.That(english, Contains.Key("Action_map1_stickAction"));
|
||||
Assert.That(english, Contains.Key("Action_map2_vector2Action"));
|
||||
Assert.That(english["Set_map1"], Is.EqualTo("map1"));
|
||||
Assert.That(english["Set_map2"], Is.EqualTo("map2"));
|
||||
Assert.That(english["Action_map1_buttonAction"], Is.EqualTo("buttonAction"));
|
||||
Assert.That(english["Action_map1_axisAction"], Is.EqualTo("axisAction"));
|
||||
Assert.That(english["Action_map1_stickAction"], Is.EqualTo("stickAction"));
|
||||
Assert.That(english["Action_map2_vector2Action"], Is.EqualTo("vector2Action"));
|
||||
Assert.That(english.Count, Is.EqualTo(6));
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 471f8fe5e815749d9bf78d2517877ff4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -645,7 +645,9 @@ PlayerSettings:
|
|||
webGLUseEmbeddedResources: 0
|
||||
webGLCompressionFormat: 1
|
||||
webGLLinkerTarget: 0
|
||||
scriptingDefineSymbols: {}
|
||||
webGLThreadsSupport: 0
|
||||
scriptingDefineSymbols:
|
||||
1: UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT;UNITY_ENABLE_STEAMWORKSNET_STEAM_CONTROLLER_SUPPORT
|
||||
platformArchitecture:
|
||||
iOS: 0
|
||||
scriptingBackend:
|
||||
|
@ -654,6 +656,7 @@ PlayerSettings:
|
|||
iOS: 1
|
||||
il2cppCompilerConfiguration:
|
||||
Android: 0
|
||||
managedStrippingLevel: {}
|
||||
incrementalIl2cppBuild: {}
|
||||
allowUnsafeCode: 0
|
||||
additionalIl2CppArgs:
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
480
|
Загрузка…
Ссылка в новой задаче