Initial commit: Client Doc docs Server Tools

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ud18010
2026-07-10 10:24:29 +08:00
co-authored by Cursor
commit 7e35d8da31
3374 changed files with 680813 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 000631a5024c634438fecb92121983b6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 34192c5e0d14aee43a0e86cc4823268a
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4f007001a22b3d24dae350342c4d19c8
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,30 @@
fileFormatVersion: 2
guid: a811bde74b26b53498b4f6d872b09b6d
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
isPreloaded: 0
isOverridable: 0
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c8581f6366c00c84b9ed73520b972539
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,98 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>DOTweenEditor</name>
</assembly>
<members>
<member name="M:DG.DOTweenEditor.DOTweenEditorPreview.Start(System.Action)">
<summary>
Starts the update loop of tween in the editor. Has no effect during playMode.
</summary>
<param name="onPreviewUpdated">Eventual callback to call after every update</param>
</member>
<member name="M:DG.DOTweenEditor.DOTweenEditorPreview.Stop(System.Boolean)">
<summary>
Stops the update loop and clears the onPreviewUpdated callback.
</summary>
<param name="resetTweenTargets">If TRUE also resets the tweened objects to their original state</param>
</member>
<member name="M:DG.DOTweenEditor.DOTweenEditorPreview.PrepareTweenForPreview(DG.Tweening.Tween,System.Boolean,System.Boolean,System.Boolean)">
<summary>
Readies the tween for editor preview by setting its UpdateType to Manual plus eventual extra settings.
</summary>
<param name="t">The tween to ready</param>
<param name="clearCallbacks">If TRUE (recommended) removes all callbacks (OnComplete/Rewind/etc)</param>
<param name="preventAutoKill">If TRUE prevents the tween from being auto-killed at completion</param>
<param name="andPlay">If TRUE starts playing the tween immediately</param>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.SetEditorTexture(UnityEngine.Texture2D,UnityEngine.FilterMode,System.Int32)">
<summary>
Checks that the given editor texture use the correct import settings,
and applies them if they're incorrect.
</summary>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.DOTweenSetupRequired">
<summary>
Returns TRUE if setup is required
</summary>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.AssetExists(System.String)">
<summary>
Returns TRUE if the file/directory at the given path exists.
</summary>
<param name="adbPath">Path, relative to Unity's project folder</param>
<returns></returns>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.ADBPathToFullPath(System.String)">
<summary>
Converts the given project-relative path to a full path,
with backward (\) slashes).
</summary>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.FullPathToADBPath(System.String)">
<summary>
Converts the given full path to a path usable with AssetDatabase methods
(relative to Unity's project folder, and with the correct Unity forward (/) slashes).
</summary>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.ConnectToSourceAsset``1(System.String,System.Boolean)">
<summary>
Connects to a <see cref="T:UnityEngine.ScriptableObject"/> asset.
If the asset already exists at the given path, loads it and returns it.
Otherwise, either returns NULL or automatically creates it before loading and returning it
(depending on the given parameters).
</summary>
<typeparam name="T">Asset type</typeparam>
<param name="adbFilePath">File path (relative to Unity's project folder)</param>
<param name="createIfMissing">If TRUE and the requested asset doesn't exist, forces its creation</param>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.GetAssemblyFilePath(System.Reflection.Assembly)">
<summary>
Full path for the given loaded assembly, assembly file included
</summary>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.AddGlobalDefine(System.String)">
<summary>
Adds the given global define if it's not already present
</summary>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.RemoveGlobalDefine(System.String)">
<summary>
Removes the given global define if it's present
</summary>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.HasGlobalDefine(System.String,System.Nullable{UnityEditor.BuildTargetGroup})">
<summary>
Returns TRUE if the given global define is present in all the <see cref="T:UnityEditor.BuildTargetGroup"/>
or only in the given <see cref="T:UnityEditor.BuildTargetGroup"/>, depending on passed parameters.<para/>
</summary>
<param name="id"></param>
<param name="buildTargetGroup"><see cref="T:UnityEditor.BuildTargetGroup"/>to use. Leave NULL to check in all of them.</param>
</member>
<member name="T:DG.DOTweenEditor.DOTweenDefines">
<summary>
Not used as menu item anymore, but as a utiity function
</summary>
</member>
</members>
</doc>
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2e2c6224d345d9249acfa6e8ef40bb2d
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8f46310a8b0a8f04a92993c37c713243
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,30 @@
fileFormatVersion: 2
guid: 45d5034162d6cf04dbe46da84fc7d074
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
isPreloaded: 0
isOverridable: 0
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2955000114b35754688f12a5ac570b6e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,116 @@
fileFormatVersion: 2
guid: 8da095e39e9b4df488dfd436f81116d6
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 1
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 128
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 1
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: 56
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: 56
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData: 20201026
assetBundleName:
assetBundleVariant:
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

@@ -0,0 +1,116 @@
fileFormatVersion: 2
guid: 7051dba417b3d53409f2918f1ea4938d
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 1
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 256
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 1
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 256
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 256
resizeAlgorithm: 0
textureFormat: 56
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 256
resizeAlgorithm: 0
textureFormat: 56
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData: 20201026
assetBundleName:
assetBundleVariant:
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

@@ -0,0 +1,116 @@
fileFormatVersion: 2
guid: 519694efe2bb2914788b151fbd8c01f4
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: -1
maxTextureSize: 1024
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: -1
wrapV: -1
wrapW: -1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 1
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 1024
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 1024
resizeAlgorithm: 0
textureFormat: 56
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 1024
resizeAlgorithm: 0
textureFormat: 56
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData: 20201026
assetBundleName:
assetBundleVariant:
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

@@ -0,0 +1,116 @@
fileFormatVersion: 2
guid: 78a59ca99f8987941adb61f9e14a06a7
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 1
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 512
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 1
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 512
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 512
resizeAlgorithm: 0
textureFormat: 50
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 512
resizeAlgorithm: 0
textureFormat: 50
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 1
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData: 20201026
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cd662ef2d33664241a05451761a07b70
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,202 @@
// Author: Daniele Giardini - http://www.demigiant.com
// Created: 2018/07/13
#if true // MODULE_MARKER
using System;
using DG.Tweening.Core;
using DG.Tweening.Plugins.Options;
using UnityEngine;
#if UNITY_5 || UNITY_2017_1_OR_NEWER
using UnityEngine.Audio; // Required for AudioMixer
#endif
#pragma warning disable 1591
namespace DG.Tweening
{
public static class DOTweenModuleAudio
{
#region Shortcuts
#region Audio
/// <summary>Tweens an AudioSource's volume to the given value.
/// Also stores the AudioSource as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach (0 to 1)</param><param name="duration">The duration of the tween</param>
public static TweenerCore<float, float, FloatOptions> DOFade(this AudioSource target, float endValue, float duration)
{
if (endValue < 0) endValue = 0;
else if (endValue > 1) endValue = 1;
TweenerCore<float, float, FloatOptions> t = DOTween.To(() => target.volume, x => target.volume = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens an AudioSource's pitch to the given value.
/// Also stores the AudioSource as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<float, float, FloatOptions> DOPitch(this AudioSource target, float endValue, float duration)
{
TweenerCore<float, float, FloatOptions> t = DOTween.To(() => target.pitch, x => target.pitch = x, endValue, duration);
t.SetTarget(target);
return t;
}
#endregion
#if UNITY_5 || UNITY_2017_1_OR_NEWER
#region AudioMixer (Unity 5 or Newer)
/// <summary>Tweens an AudioMixer's exposed float to the given value.
/// Also stores the AudioMixer as the tween's target so it can be used for filtered operations.
/// Note that you need to manually expose a float in an AudioMixerGroup in order to be able to tween it from an AudioMixer.</summary>
/// <param name="floatName">Name given to the exposed float to set</param>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<float, float, FloatOptions> DOSetFloat(this AudioMixer target, string floatName, float endValue, float duration)
{
TweenerCore<float, float, FloatOptions> t = DOTween.To(()=> {
float currVal;
target.GetFloat(floatName, out currVal);
return currVal;
}, x=> target.SetFloat(floatName, x), endValue, duration);
t.SetTarget(target);
return t;
}
#region Operation Shortcuts
/// <summary>
/// Completes all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens completed
/// (meaning the tweens that don't have infinite loops and were not already complete)
/// </summary>
/// <param name="withCallbacks">For Sequences only: if TRUE also internal Sequence callbacks will be fired,
/// otherwise they will be ignored</param>
public static int DOComplete(this AudioMixer target, bool withCallbacks = false)
{
return DOTween.Complete(target, withCallbacks);
}
/// <summary>
/// Kills all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens killed.
/// </summary>
/// <param name="complete">If TRUE completes the tween before killing it</param>
public static int DOKill(this AudioMixer target, bool complete = false)
{
return DOTween.Kill(target, complete);
}
/// <summary>
/// Flips the direction (backwards if it was going forward or viceversa) of all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens flipped.
/// </summary>
public static int DOFlip(this AudioMixer target)
{
return DOTween.Flip(target);
}
/// <summary>
/// Sends to the given position all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens involved.
/// </summary>
/// <param name="to">Time position to reach
/// (if higher than the whole tween duration the tween will simply reach its end)</param>
/// <param name="andPlay">If TRUE will play the tween after reaching the given position, otherwise it will pause it</param>
public static int DOGoto(this AudioMixer target, float to, bool andPlay = false)
{
return DOTween.Goto(target, to, andPlay);
}
/// <summary>
/// Pauses all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens paused.
/// </summary>
public static int DOPause(this AudioMixer target)
{
return DOTween.Pause(target);
}
/// <summary>
/// Plays all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens played.
/// </summary>
public static int DOPlay(this AudioMixer target)
{
return DOTween.Play(target);
}
/// <summary>
/// Plays backwards all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens played.
/// </summary>
public static int DOPlayBackwards(this AudioMixer target)
{
return DOTween.PlayBackwards(target);
}
/// <summary>
/// Plays forward all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens played.
/// </summary>
public static int DOPlayForward(this AudioMixer target)
{
return DOTween.PlayForward(target);
}
/// <summary>
/// Restarts all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens restarted.
/// </summary>
public static int DORestart(this AudioMixer target)
{
return DOTween.Restart(target);
}
/// <summary>
/// Rewinds all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens rewinded.
/// </summary>
public static int DORewind(this AudioMixer target)
{
return DOTween.Rewind(target);
}
/// <summary>
/// Smoothly rewinds all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens rewinded.
/// </summary>
public static int DOSmoothRewind(this AudioMixer target)
{
return DOTween.SmoothRewind(target);
}
/// <summary>
/// Toggles the paused state (plays if it was paused, pauses if it was playing) of all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens involved.
/// </summary>
public static int DOTogglePause(this AudioMixer target)
{
return DOTween.TogglePause(target);
}
#endregion
#endregion
#endif
#endregion
}
}
#endif
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b766d08851589514b97afb23c6f30a70
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,216 @@
// Author: Daniele Giardini - http://www.demigiant.com
// Created: 2018/07/13
#if true // MODULE_MARKER
using System;
using DG.Tweening.Core;
using DG.Tweening.Core.Enums;
using DG.Tweening.Plugins;
using DG.Tweening.Plugins.Core.PathCore;
using DG.Tweening.Plugins.Options;
using UnityEngine;
#pragma warning disable 1591
namespace DG.Tweening
{
public static class DOTweenModulePhysics
{
#region Shortcuts
#region Rigidbody
/// <summary>Tweens a Rigidbody's position to the given value.
/// Also stores the rigidbody as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOMove(this Rigidbody target, Vector3 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a Rigidbody's X position to the given value.
/// Also stores the rigidbody as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOMoveX(this Rigidbody target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, new Vector3(endValue, 0, 0), duration);
t.SetOptions(AxisConstraint.X, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a Rigidbody's Y position to the given value.
/// Also stores the rigidbody as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOMoveY(this Rigidbody target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, new Vector3(0, endValue, 0), duration);
t.SetOptions(AxisConstraint.Y, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a Rigidbody's Z position to the given value.
/// Also stores the rigidbody as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOMoveZ(this Rigidbody target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, new Vector3(0, 0, endValue), duration);
t.SetOptions(AxisConstraint.Z, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a Rigidbody's rotation to the given value.
/// Also stores the rigidbody as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="mode">Rotation mode</param>
public static TweenerCore<Quaternion, Vector3, QuaternionOptions> DORotate(this Rigidbody target, Vector3 endValue, float duration, RotateMode mode = RotateMode.Fast)
{
TweenerCore<Quaternion, Vector3, QuaternionOptions> t = DOTween.To(() => target.rotation, target.MoveRotation, endValue, duration);
t.SetTarget(target);
t.plugOptions.rotateMode = mode;
return t;
}
/// <summary>Tweens a Rigidbody's rotation so that it will look towards the given position.
/// Also stores the rigidbody as the tween's target so it can be used for filtered operations</summary>
/// <param name="towards">The position to look at</param><param name="duration">The duration of the tween</param>
/// <param name="axisConstraint">Eventual axis constraint for the rotation</param>
/// <param name="up">The vector that defines in which direction up is (default: Vector3.up)</param>
public static TweenerCore<Quaternion, Vector3, QuaternionOptions> DOLookAt(this Rigidbody target, Vector3 towards, float duration, AxisConstraint axisConstraint = AxisConstraint.None, Vector3? up = null)
{
TweenerCore<Quaternion, Vector3, QuaternionOptions> t = DOTween.To(() => target.rotation, target.MoveRotation, towards, duration)
.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetLookAt);
t.plugOptions.axisConstraint = axisConstraint;
t.plugOptions.up = (up == null) ? Vector3.up : (Vector3)up;
return t;
}
#region Special
/// <summary>Tweens a Rigidbody's position to the given value, while also applying a jump effect along the Y axis.
/// Returns a Sequence instead of a Tweener.
/// Also stores the Rigidbody as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param>
/// <param name="jumpPower">Power of the jump (the max height of the jump is represented by this plus the final Y offset)</param>
/// <param name="numJumps">Total number of jumps</param>
/// <param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static Sequence DOJump(this Rigidbody target, Vector3 endValue, float jumpPower, int numJumps, float duration, bool snapping = false)
{
if (numJumps < 1) numJumps = 1;
float startPosY = 0;
float offsetY = -1;
bool offsetYSet = false;
Sequence s = DOTween.Sequence();
Tween yTween = DOTween.To(() => target.position, target.MovePosition, new Vector3(0, jumpPower, 0), duration / (numJumps * 2))
.SetOptions(AxisConstraint.Y, snapping).SetEase(Ease.OutQuad).SetRelative()
.SetLoops(numJumps * 2, LoopType.Yoyo)
.OnStart(() => startPosY = target.position.y);
s.Append(DOTween.To(() => target.position, target.MovePosition, new Vector3(endValue.x, 0, 0), duration)
.SetOptions(AxisConstraint.X, snapping).SetEase(Ease.Linear)
).Join(DOTween.To(() => target.position, target.MovePosition, new Vector3(0, 0, endValue.z), duration)
.SetOptions(AxisConstraint.Z, snapping).SetEase(Ease.Linear)
).Join(yTween)
.SetTarget(target).SetEase(DOTween.defaultEaseType);
yTween.OnUpdate(() => {
if (!offsetYSet) {
offsetYSet = true;
offsetY = s.isRelative ? endValue.y : endValue.y - startPosY;
}
Vector3 pos = target.position;
pos.y += DOVirtual.EasedValue(0, offsetY, yTween.ElapsedPercentage(), Ease.OutQuad);
target.MovePosition(pos);
});
return s;
}
/// <summary>Tweens a Rigidbody's position through the given path waypoints, using the chosen path algorithm.
/// Also stores the Rigidbody as the tween's target so it can be used for filtered operations.
/// <para>NOTE: to tween a rigidbody correctly it should be set to kinematic at least while being tweened.</para>
/// <para>BEWARE: doesn't work on Windows Phone store (waiting for Unity to fix their own bug).
/// If you plan to publish there you should use a regular transform.DOPath.</para></summary>
/// <param name="path">The waypoints to go through</param>
/// <param name="duration">The duration of the tween</param>
/// <param name="pathType">The type of path: Linear (straight path) or CatmullRom (curved CatmullRom path)</param>
/// <param name="pathMode">The path mode: 3D, side-scroller 2D, top-down 2D</param>
/// <param name="resolution">The resolution of the path (useless in case of Linear paths): higher resolutions make for more detailed curved paths but are more expensive.
/// Defaults to 10, but a value of 5 is usually enough if you don't have dramatic long curves between waypoints</param>
/// <param name="gizmoColor">The color of the path (shown when gizmos are active in the Play panel and the tween is running)</param>
public static TweenerCore<Vector3, Path, PathOptions> DOPath(
this Rigidbody target, Vector3[] path, float duration, PathType pathType = PathType.Linear,
PathMode pathMode = PathMode.Full3D, int resolution = 10, Color? gizmoColor = null
)
{
if (resolution < 1) resolution = 1;
TweenerCore<Vector3, Path, PathOptions> t = DOTween.To(PathPlugin.Get(), () => target.position, target.MovePosition, new Path(pathType, path, resolution, gizmoColor), duration)
.SetTarget(target).SetUpdate(UpdateType.Fixed);
t.plugOptions.isRigidbody = true;
t.plugOptions.mode = pathMode;
return t;
}
/// <summary>Tweens a Rigidbody's localPosition through the given path waypoints, using the chosen path algorithm.
/// Also stores the Rigidbody as the tween's target so it can be used for filtered operations
/// <para>NOTE: to tween a rigidbody correctly it should be set to kinematic at least while being tweened.</para>
/// <para>BEWARE: doesn't work on Windows Phone store (waiting for Unity to fix their own bug).
/// If you plan to publish there you should use a regular transform.DOLocalPath.</para></summary>
/// <param name="path">The waypoint to go through</param>
/// <param name="duration">The duration of the tween</param>
/// <param name="pathType">The type of path: Linear (straight path) or CatmullRom (curved CatmullRom path)</param>
/// <param name="pathMode">The path mode: 3D, side-scroller 2D, top-down 2D</param>
/// <param name="resolution">The resolution of the path: higher resolutions make for more detailed curved paths but are more expensive.
/// Defaults to 10, but a value of 5 is usually enough if you don't have dramatic long curves between waypoints</param>
/// <param name="gizmoColor">The color of the path (shown when gizmos are active in the Play panel and the tween is running)</param>
public static TweenerCore<Vector3, Path, PathOptions> DOLocalPath(
this Rigidbody target, Vector3[] path, float duration, PathType pathType = PathType.Linear,
PathMode pathMode = PathMode.Full3D, int resolution = 10, Color? gizmoColor = null
)
{
if (resolution < 1) resolution = 1;
Transform trans = target.transform;
TweenerCore<Vector3, Path, PathOptions> t = DOTween.To(PathPlugin.Get(), () => trans.localPosition, x => target.MovePosition(trans.parent == null ? x : trans.parent.TransformPoint(x)), new Path(pathType, path, resolution, gizmoColor), duration)
.SetTarget(target).SetUpdate(UpdateType.Fixed);
t.plugOptions.isRigidbody = true;
t.plugOptions.mode = pathMode;
t.plugOptions.useLocalPosition = true;
return t;
}
// Used by path editor when creating the actual tween, so it can pass a pre-compiled path
internal static TweenerCore<Vector3, Path, PathOptions> DOPath(
this Rigidbody target, Path path, float duration, PathMode pathMode = PathMode.Full3D
)
{
TweenerCore<Vector3, Path, PathOptions> t = DOTween.To(PathPlugin.Get(), () => target.position, target.MovePosition, path, duration)
.SetTarget(target);
t.plugOptions.isRigidbody = true;
t.plugOptions.mode = pathMode;
return t;
}
internal static TweenerCore<Vector3, Path, PathOptions> DOLocalPath(
this Rigidbody target, Path path, float duration, PathMode pathMode = PathMode.Full3D
)
{
Transform trans = target.transform;
TweenerCore<Vector3, Path, PathOptions> t = DOTween.To(PathPlugin.Get(), () => trans.localPosition, x => target.MovePosition(trans.parent == null ? x : trans.parent.TransformPoint(x)), path, duration)
.SetTarget(target);
t.plugOptions.isRigidbody = true;
t.plugOptions.mode = pathMode;
t.plugOptions.useLocalPosition = true;
return t;
}
#endregion
#endregion
#endregion
}
}
#endif
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dae9aa560b4242648a3affa2bfabc365
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,107 @@
// Author: Daniele Giardini - http://www.demigiant.com
// Created: 2018/07/13
#if true && (UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_5 || UNITY_2017_1_OR_NEWER) // MODULE_MARKER
using System;
using DG.Tweening.Core;
using DG.Tweening.Plugins.Options;
using UnityEngine;
#pragma warning disable 1591
namespace DG.Tweening
{
public static class DOTweenModulePhysics2D
{
#region Shortcuts
#region Rigidbody2D Shortcuts
/// <summary>Tweens a Rigidbody2D's position to the given value.
/// Also stores the Rigidbody2D as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOMove(this Rigidbody2D target, Vector2 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a Rigidbody2D's X position to the given value.
/// Also stores the Rigidbody2D as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOMoveX(this Rigidbody2D target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, new Vector2(endValue, 0), duration);
t.SetOptions(AxisConstraint.X, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a Rigidbody2D's Y position to the given value.
/// Also stores the Rigidbody2D as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOMoveY(this Rigidbody2D target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, new Vector2(0, endValue), duration);
t.SetOptions(AxisConstraint.Y, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a Rigidbody2D's rotation to the given value.
/// Also stores the Rigidbody2D as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<float, float, FloatOptions> DORotate(this Rigidbody2D target, float endValue, float duration)
{
TweenerCore<float, float, FloatOptions> t = DOTween.To(() => target.rotation, target.MoveRotation, endValue, duration);
t.SetTarget(target);
return t;
}
#region Special
/// <summary>Tweens a Rigidbody2D's position to the given value, while also applying a jump effect along the Y axis.
/// Returns a Sequence instead of a Tweener.
/// Also stores the Rigidbody2D as the tween's target so it can be used for filtered operations.
/// <para>IMPORTANT: a rigidbody2D can't be animated in a jump arc using MovePosition, so the tween will directly set the position</para></summary>
/// <param name="endValue">The end value to reach</param>
/// <param name="jumpPower">Power of the jump (the max height of the jump is represented by this plus the final Y offset)</param>
/// <param name="numJumps">Total number of jumps</param>
/// <param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static Sequence DOJump(this Rigidbody2D target, Vector2 endValue, float jumpPower, int numJumps, float duration, bool snapping = false)
{
if (numJumps < 1) numJumps = 1;
float startPosY = 0;
float offsetY = -1;
bool offsetYSet = false;
Sequence s = DOTween.Sequence();
Tween yTween = DOTween.To(() => target.position, x => target.position = x, new Vector2(0, jumpPower), duration / (numJumps * 2))
.SetOptions(AxisConstraint.Y, snapping).SetEase(Ease.OutQuad).SetRelative()
.SetLoops(numJumps * 2, LoopType.Yoyo)
.OnStart(() => startPosY = target.position.y);
s.Append(DOTween.To(() => target.position, x => target.position = x, new Vector2(endValue.x, 0), duration)
.SetOptions(AxisConstraint.X, snapping).SetEase(Ease.Linear)
).Join(yTween)
.SetTarget(target).SetEase(DOTween.defaultEaseType);
yTween.OnUpdate(() => {
if (!offsetYSet) {
offsetYSet = true;
offsetY = s.isRelative ? endValue.y : endValue.y - startPosY;
}
Vector3 pos = target.position;
pos.y += DOVirtual.EasedValue(0, offsetY, yTween.ElapsedPercentage(), Ease.OutQuad);
target.MovePosition(pos);
});
return s;
}
#endregion
#endregion
#endregion
}
}
#endif
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 230fe34542e175245ba74b4659dae700
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,93 @@
// Author: Daniele Giardini - http://www.demigiant.com
// Created: 2018/07/13
#if true && (UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_5 || UNITY_2017_1_OR_NEWER) // MODULE_MARKER
using System;
using UnityEngine;
using DG.Tweening.Core;
using DG.Tweening.Plugins.Options;
#pragma warning disable 1591
namespace DG.Tweening
{
public static class DOTweenModuleSprite
{
#region Shortcuts
#region SpriteRenderer
/// <summary>Tweens a SpriteRenderer's color to the given value.
/// Also stores the spriteRenderer as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOColor(this SpriteRenderer target, Color endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.To(() => target.color, x => target.color = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens a Material's alpha color to the given value.
/// Also stores the spriteRenderer as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOFade(this SpriteRenderer target, float endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.ToAlpha(() => target.color, x => target.color = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens a SpriteRenderer's color using the given gradient
/// (NOTE 1: only uses the colors of the gradient, not the alphas - NOTE 2: creates a Sequence, not a Tweener).
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
/// <param name="gradient">The gradient to use</param><param name="duration">The duration of the tween</param>
public static Sequence DOGradientColor(this SpriteRenderer target, Gradient gradient, float duration)
{
Sequence s = DOTween.Sequence();
GradientColorKey[] colors = gradient.colorKeys;
int len = colors.Length;
for (int i = 0; i < len; ++i) {
GradientColorKey c = colors[i];
if (i == 0 && c.time <= 0) {
target.color = c.color;
continue;
}
float colorDuration = i == len - 1
? duration - s.Duration(false) // Verifies that total duration is correct
: duration * (i == 0 ? c.time : c.time - colors[i - 1].time);
s.Append(target.DOColor(c.color, colorDuration).SetEase(Ease.Linear));
}
s.SetTarget(target);
return s;
}
#endregion
#region Blendables
#region SpriteRenderer
/// <summary>Tweens a SpriteRenderer's color to the given value,
/// in a way that allows other DOBlendableColor tweens to work together on the same target,
/// instead than fight each other as multiple DOColor would do.
/// Also stores the SpriteRenderer as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The value to tween to</param><param name="duration">The duration of the tween</param>
public static Tweener DOBlendableColor(this SpriteRenderer target, Color endValue, float duration)
{
endValue = endValue - target.color;
Color to = new Color(0, 0, 0, 0);
return DOTween.To(() => to, x => {
Color diff = x - to;
to = x;
target.color += diff;
}, endValue, duration)
.Blendable().SetTarget(target);
}
#endregion
#endregion
#endregion
}
}
#endif
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 188918ab119d93148aa0de59ccf5286b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,609 @@
// Author: Daniele Giardini - http://www.demigiant.com
// Created: 2018/07/13
#if true && (UNITY_4_6 || UNITY_5 || UNITY_2017_1_OR_NEWER) // MODULE_MARKER
using System;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening.Core;
using DG.Tweening.Core.Enums;
using DG.Tweening.Plugins.Options;
#pragma warning disable 1591
namespace DG.Tweening
{
public static class DOTweenModuleUI
{
#region Shortcuts
#region CanvasGroup
/// <summary>Tweens a CanvasGroup's alpha color to the given value.
/// Also stores the canvasGroup as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<float, float, FloatOptions> DOFade(this CanvasGroup target, float endValue, float duration)
{
TweenerCore<float, float, FloatOptions> t = DOTween.To(() => target.alpha, x => target.alpha = x, endValue, duration);
t.SetTarget(target);
return t;
}
#endregion
#region Graphic
/// <summary>Tweens an Graphic's color to the given value.
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOColor(this Graphic target, Color endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.To(() => target.color, x => target.color = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens an Graphic's alpha color to the given value.
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOFade(this Graphic target, float endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.ToAlpha(() => target.color, x => target.color = x, endValue, duration);
t.SetTarget(target);
return t;
}
#endregion
#region Image
/// <summary>Tweens an Image's color to the given value.
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOColor(this Image target, Color endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.To(() => target.color, x => target.color = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens an Image's alpha color to the given value.
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOFade(this Image target, float endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.ToAlpha(() => target.color, x => target.color = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens an Image's fillAmount to the given value.
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach (0 to 1)</param><param name="duration">The duration of the tween</param>
public static TweenerCore<float, float, FloatOptions> DOFillAmount(this Image target, float endValue, float duration)
{
if (endValue > 1) endValue = 1;
else if (endValue < 0) endValue = 0;
TweenerCore<float, float, FloatOptions> t = DOTween.To(() => target.fillAmount, x => target.fillAmount = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens an Image's colors using the given gradient
/// (NOTE 1: only uses the colors of the gradient, not the alphas - NOTE 2: creates a Sequence, not a Tweener).
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
/// <param name="gradient">The gradient to use</param><param name="duration">The duration of the tween</param>
public static Sequence DOGradientColor(this Image target, Gradient gradient, float duration)
{
Sequence s = DOTween.Sequence();
GradientColorKey[] colors = gradient.colorKeys;
int len = colors.Length;
for (int i = 0; i < len; ++i) {
GradientColorKey c = colors[i];
if (i == 0 && c.time <= 0) {
target.color = c.color;
continue;
}
float colorDuration = i == len - 1
? duration - s.Duration(false) // Verifies that total duration is correct
: duration * (i == 0 ? c.time : c.time - colors[i - 1].time);
s.Append(target.DOColor(c.color, colorDuration).SetEase(Ease.Linear));
}
s.SetTarget(target);
return s;
}
#endregion
#region LayoutElement
/// <summary>Tweens an LayoutElement's flexibleWidth/Height to the given value.
/// Also stores the LayoutElement as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOFlexibleSize(this LayoutElement target, Vector2 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => new Vector2(target.flexibleWidth, target.flexibleHeight), x => {
target.flexibleWidth = x.x;
target.flexibleHeight = x.y;
}, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Tweens an LayoutElement's minWidth/Height to the given value.
/// Also stores the LayoutElement as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOMinSize(this LayoutElement target, Vector2 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => new Vector2(target.minWidth, target.minHeight), x => {
target.minWidth = x.x;
target.minHeight = x.y;
}, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Tweens an LayoutElement's preferredWidth/Height to the given value.
/// Also stores the LayoutElement as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOPreferredSize(this LayoutElement target, Vector2 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => new Vector2(target.preferredWidth, target.preferredHeight), x => {
target.preferredWidth = x.x;
target.preferredHeight = x.y;
}, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
#endregion
#region Outline
/// <summary>Tweens a Outline's effectColor to the given value.
/// Also stores the Outline as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOColor(this Outline target, Color endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.To(() => target.effectColor, x => target.effectColor = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens a Outline's effectColor alpha to the given value.
/// Also stores the Outline as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOFade(this Outline target, float endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.ToAlpha(() => target.effectColor, x => target.effectColor = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens a Outline's effectDistance to the given value.
/// Also stores the Outline as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOScale(this Outline target, Vector2 endValue, float duration)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.effectDistance, x => target.effectDistance = x, endValue, duration);
t.SetTarget(target);
return t;
}
#endregion
#region RectTransform
/// <summary>Tweens a RectTransform's anchoredPosition to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOAnchorPos(this RectTransform target, Vector2 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.anchoredPosition, x => target.anchoredPosition = x, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's anchoredPosition X to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOAnchorPosX(this RectTransform target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.anchoredPosition, x => target.anchoredPosition = x, new Vector2(endValue, 0), duration);
t.SetOptions(AxisConstraint.X, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's anchoredPosition Y to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOAnchorPosY(this RectTransform target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.anchoredPosition, x => target.anchoredPosition = x, new Vector2(0, endValue), duration);
t.SetOptions(AxisConstraint.Y, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's anchoredPosition3D to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOAnchorPos3D(this RectTransform target, Vector3 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.anchoredPosition3D, x => target.anchoredPosition3D = x, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's anchoredPosition3D X to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOAnchorPos3DX(this RectTransform target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.anchoredPosition3D, x => target.anchoredPosition3D = x, new Vector3(endValue, 0, 0), duration);
t.SetOptions(AxisConstraint.X, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's anchoredPosition3D Y to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOAnchorPos3DY(this RectTransform target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.anchoredPosition3D, x => target.anchoredPosition3D = x, new Vector3(0, endValue, 0), duration);
t.SetOptions(AxisConstraint.Y, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's anchoredPosition3D Z to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOAnchorPos3DZ(this RectTransform target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.anchoredPosition3D, x => target.anchoredPosition3D = x, new Vector3(0, 0, endValue), duration);
t.SetOptions(AxisConstraint.Z, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's anchorMax to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOAnchorMax(this RectTransform target, Vector2 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.anchorMax, x => target.anchorMax = x, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's anchorMin to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOAnchorMin(this RectTransform target, Vector2 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.anchorMin, x => target.anchorMin = x, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's pivot to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOPivot(this RectTransform target, Vector2 endValue, float duration)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.pivot, x => target.pivot = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's pivot X to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOPivotX(this RectTransform target, float endValue, float duration)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.pivot, x => target.pivot = x, new Vector2(endValue, 0), duration);
t.SetOptions(AxisConstraint.X).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's pivot Y to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOPivotY(this RectTransform target, float endValue, float duration)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.pivot, x => target.pivot = x, new Vector2(0, endValue), duration);
t.SetOptions(AxisConstraint.Y).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's sizeDelta to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOSizeDelta(this RectTransform target, Vector2 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.sizeDelta, x => target.sizeDelta = x, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Punches a RectTransform's anchoredPosition towards the given direction and then back to the starting one
/// as if it was connected to the starting position via an elastic.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="punch">The direction and strength of the punch (added to the RectTransform's current position)</param>
/// <param name="duration">The duration of the tween</param>
/// <param name="vibrato">Indicates how much will the punch vibrate</param>
/// <param name="elasticity">Represents how much (0 to 1) the vector will go beyond the starting position when bouncing backwards.
/// 1 creates a full oscillation between the punch direction and the opposite direction,
/// while 0 oscillates only between the punch and the start position</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static Tweener DOPunchAnchorPos(this RectTransform target, Vector2 punch, float duration, int vibrato = 10, float elasticity = 1, bool snapping = false)
{
return DOTween.Punch(() => target.anchoredPosition, x => target.anchoredPosition = x, punch, duration, vibrato, elasticity)
.SetTarget(target).SetOptions(snapping);
}
/// <summary>Shakes a RectTransform's anchoredPosition with the given values.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="duration">The duration of the tween</param>
/// <param name="strength">The shake strength</param>
/// <param name="vibrato">Indicates how much will the shake vibrate</param>
/// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware).
/// Setting it to 0 will shake along a single direction.</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
/// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
public static Tweener DOShakeAnchorPos(this RectTransform target, float duration, float strength = 100, int vibrato = 10, float randomness = 90, bool snapping = false, bool fadeOut = true)
{
return DOTween.Shake(() => target.anchoredPosition, x => target.anchoredPosition = x, duration, strength, vibrato, randomness, true, fadeOut)
.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetShake).SetOptions(snapping);
}
/// <summary>Shakes a RectTransform's anchoredPosition with the given values.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="duration">The duration of the tween</param>
/// <param name="strength">The shake strength on each axis</param>
/// <param name="vibrato">Indicates how much will the shake vibrate</param>
/// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware).
/// Setting it to 0 will shake along a single direction.</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
/// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
public static Tweener DOShakeAnchorPos(this RectTransform target, float duration, Vector2 strength, int vibrato = 10, float randomness = 90, bool snapping = false, bool fadeOut = true)
{
return DOTween.Shake(() => target.anchoredPosition, x => target.anchoredPosition = x, duration, strength, vibrato, randomness, fadeOut)
.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetShake).SetOptions(snapping);
}
#region Special
/// <summary>Tweens a RectTransform's anchoredPosition to the given value, while also applying a jump effect along the Y axis.
/// Returns a Sequence instead of a Tweener.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param>
/// <param name="jumpPower">Power of the jump (the max height of the jump is represented by this plus the final Y offset)</param>
/// <param name="numJumps">Total number of jumps</param>
/// <param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static Sequence DOJumpAnchorPos(this RectTransform target, Vector2 endValue, float jumpPower, int numJumps, float duration, bool snapping = false)
{
if (numJumps < 1) numJumps = 1;
float startPosY = 0;
float offsetY = -1;
bool offsetYSet = false;
// Separate Y Tween so we can elaborate elapsedPercentage on that insted of on the Sequence
// (in case users add a delay or other elements to the Sequence)
Sequence s = DOTween.Sequence();
Tween yTween = DOTween.To(() => target.anchoredPosition, x => target.anchoredPosition = x, new Vector2(0, jumpPower), duration / (numJumps * 2))
.SetOptions(AxisConstraint.Y, snapping).SetEase(Ease.OutQuad).SetRelative()
.SetLoops(numJumps * 2, LoopType.Yoyo)
.OnStart(()=> startPosY = target.anchoredPosition.y);
s.Append(DOTween.To(() => target.anchoredPosition, x => target.anchoredPosition = x, new Vector2(endValue.x, 0), duration)
.SetOptions(AxisConstraint.X, snapping).SetEase(Ease.Linear)
).Join(yTween)
.SetTarget(target).SetEase(DOTween.defaultEaseType);
s.OnUpdate(() => {
if (!offsetYSet) {
offsetYSet = true;
offsetY = s.isRelative ? endValue.y : endValue.y - startPosY;
}
Vector2 pos = target.anchoredPosition;
pos.y += DOVirtual.EasedValue(0, offsetY, s.ElapsedDirectionalPercentage(), Ease.OutQuad);
target.anchoredPosition = pos;
});
return s;
}
#endregion
#endregion
#region ScrollRect
/// <summary>Tweens a ScrollRect's horizontal/verticalNormalizedPosition to the given value.
/// Also stores the ScrollRect as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static Tweener DONormalizedPos(this ScrollRect target, Vector2 endValue, float duration, bool snapping = false)
{
return DOTween.To(() => new Vector2(target.horizontalNormalizedPosition, target.verticalNormalizedPosition),
x => {
target.horizontalNormalizedPosition = x.x;
target.verticalNormalizedPosition = x.y;
}, endValue, duration)
.SetOptions(snapping).SetTarget(target);
}
/// <summary>Tweens a ScrollRect's horizontalNormalizedPosition to the given value.
/// Also stores the ScrollRect as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static Tweener DOHorizontalNormalizedPos(this ScrollRect target, float endValue, float duration, bool snapping = false)
{
return DOTween.To(() => target.horizontalNormalizedPosition, x => target.horizontalNormalizedPosition = x, endValue, duration)
.SetOptions(snapping).SetTarget(target);
}
/// <summary>Tweens a ScrollRect's verticalNormalizedPosition to the given value.
/// Also stores the ScrollRect as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static Tweener DOVerticalNormalizedPos(this ScrollRect target, float endValue, float duration, bool snapping = false)
{
return DOTween.To(() => target.verticalNormalizedPosition, x => target.verticalNormalizedPosition = x, endValue, duration)
.SetOptions(snapping).SetTarget(target);
}
#endregion
#region Slider
/// <summary>Tweens a Slider's value to the given value.
/// Also stores the Slider as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<float, float, FloatOptions> DOValue(this Slider target, float endValue, float duration, bool snapping = false)
{
TweenerCore<float, float, FloatOptions> t = DOTween.To(() => target.value, x => target.value = x, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
#endregion
#region Text
/// <summary>Tweens a Text's color to the given value.
/// Also stores the Text as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOColor(this Text target, Color endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.To(() => target.color, x => target.color = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens a Text's alpha color to the given value.
/// Also stores the Text as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOFade(this Text target, float endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.ToAlpha(() => target.color, x => target.color = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens a Text's text to the given value.
/// Also stores the Text as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end string to tween to</param><param name="duration">The duration of the tween</param>
/// <param name="richTextEnabled">If TRUE (default), rich text will be interpreted correctly while animated,
/// otherwise all tags will be considered as normal text</param>
/// <param name="scrambleMode">The type of scramble mode to use, if any</param>
/// <param name="scrambleChars">A string containing the characters to use for scrambling.
/// Use as many characters as possible (minimum 10) because DOTween uses a fast scramble mode which gives better results with more characters.
/// Leave it to NULL (default) to use default ones</param>
public static TweenerCore<string, string, StringOptions> DOText(this Text target, string endValue, float duration, bool richTextEnabled = true, ScrambleMode scrambleMode = ScrambleMode.None, string scrambleChars = null)
{
if (endValue == null) {
if (Debugger.logPriority > 0) Debugger.LogWarning("You can't pass a NULL string to DOText: an empty string will be used instead to avoid errors");
endValue = "";
}
TweenerCore<string, string, StringOptions> t = DOTween.To(() => target.text, x => target.text = x, endValue, duration);
t.SetOptions(richTextEnabled, scrambleMode, scrambleChars)
.SetTarget(target);
return t;
}
#endregion
#region Blendables
#region Graphic
/// <summary>Tweens a Graphic's color to the given value,
/// in a way that allows other DOBlendableColor tweens to work together on the same target,
/// instead than fight each other as multiple DOColor would do.
/// Also stores the Graphic as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The value to tween to</param><param name="duration">The duration of the tween</param>
public static Tweener DOBlendableColor(this Graphic target, Color endValue, float duration)
{
endValue = endValue - target.color;
Color to = new Color(0, 0, 0, 0);
return DOTween.To(() => to, x => {
Color diff = x - to;
to = x;
target.color += diff;
}, endValue, duration)
.Blendable().SetTarget(target);
}
#endregion
#region Image
/// <summary>Tweens a Image's color to the given value,
/// in a way that allows other DOBlendableColor tweens to work together on the same target,
/// instead than fight each other as multiple DOColor would do.
/// Also stores the Image as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The value to tween to</param><param name="duration">The duration of the tween</param>
public static Tweener DOBlendableColor(this Image target, Color endValue, float duration)
{
endValue = endValue - target.color;
Color to = new Color(0, 0, 0, 0);
return DOTween.To(() => to, x => {
Color diff = x - to;
to = x;
target.color += diff;
}, endValue, duration)
.Blendable().SetTarget(target);
}
#endregion
#region Text
/// <summary>Tweens a Text's color BY the given value,
/// in a way that allows other DOBlendableColor tweens to work together on the same target,
/// instead than fight each other as multiple DOColor would do.
/// Also stores the Text as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The value to tween to</param><param name="duration">The duration of the tween</param>
public static Tweener DOBlendableColor(this Text target, Color endValue, float duration)
{
endValue = endValue - target.color;
Color to = new Color(0, 0, 0, 0);
return DOTween.To(() => to, x => {
Color diff = x - to;
to = x;
target.color += diff;
}, endValue, duration)
.Blendable().SetTarget(target);
}
#endregion
#endregion
#endregion
// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
// ███ INTERNAL CLASSES ████████████████████████████████████████████████████████████████████████████████████████████████
// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
public static class Utils
{
/// <summary>
/// Converts the anchoredPosition of the first RectTransform to the second RectTransform,
/// taking into consideration offset, anchors and pivot, and returns the new anchoredPosition
/// </summary>
public static Vector2 SwitchToRectTransform(RectTransform from, RectTransform to)
{
Vector2 localPoint;
Vector2 fromPivotDerivedOffset = new Vector2(from.rect.width * 0.5f + from.rect.xMin, from.rect.height * 0.5f + from.rect.yMin);
Vector2 screenP = RectTransformUtility.WorldToScreenPoint(null, from.position);
screenP += fromPivotDerivedOffset;
RectTransformUtility.ScreenPointToLocalPointInRectangle(to, screenP, null, out localPoint);
Vector2 pivotDerivedOffset = new Vector2(to.rect.width * 0.5f + to.rect.xMin, to.rect.height * 0.5f + to.rect.yMin);
return to.anchoredPosition + localPoint - pivotDerivedOffset;
}
}
}
}
#endif
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a060394c03331a64392db53a10e7f2d1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,301 @@
// Author: Daniele Giardini - http://www.demigiant.com
// Created: 2018/07/13
using System;
using UnityEngine;
using DG.Tweening.Core;
using DG.Tweening.Plugins.Options;
#pragma warning disable 1591
namespace DG.Tweening
{
/// <summary>
/// Shortcuts/functions that are not strictly related to specific Modules
/// but are available only on some Unity versions
/// </summary>
public static class DOTweenModuleUnityVersion
{
#if UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_5 || UNITY_2017_1_OR_NEWER
#region Unity 4.3 or Newer
#region Material
/// <summary>Tweens a Material's color using the given gradient
/// (NOTE 1: only uses the colors of the gradient, not the alphas - NOTE 2: creates a Sequence, not a Tweener).
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
/// <param name="gradient">The gradient to use</param><param name="duration">The duration of the tween</param>
public static Sequence DOGradientColor(this Material target, Gradient gradient, float duration)
{
Sequence s = DOTween.Sequence();
GradientColorKey[] colors = gradient.colorKeys;
int len = colors.Length;
for (int i = 0; i < len; ++i) {
GradientColorKey c = colors[i];
if (i == 0 && c.time <= 0) {
target.color = c.color;
continue;
}
float colorDuration = i == len - 1
? duration - s.Duration(false) // Verifies that total duration is correct
: duration * (i == 0 ? c.time : c.time - colors[i - 1].time);
s.Append(target.DOColor(c.color, colorDuration).SetEase(Ease.Linear));
}
s.SetTarget(target);
return s;
}
/// <summary>Tweens a Material's named color property using the given gradient
/// (NOTE 1: only uses the colors of the gradient, not the alphas - NOTE 2: creates a Sequence, not a Tweener).
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
/// <param name="gradient">The gradient to use</param>
/// <param name="property">The name of the material property to tween (like _Tint or _SpecColor)</param>
/// <param name="duration">The duration of the tween</param>
public static Sequence DOGradientColor(this Material target, Gradient gradient, string property, float duration)
{
Sequence s = DOTween.Sequence();
GradientColorKey[] colors = gradient.colorKeys;
int len = colors.Length;
for (int i = 0; i < len; ++i) {
GradientColorKey c = colors[i];
if (i == 0 && c.time <= 0) {
target.SetColor(property, c.color);
continue;
}
float colorDuration = i == len - 1
? duration - s.Duration(false) // Verifies that total duration is correct
: duration * (i == 0 ? c.time : c.time - colors[i - 1].time);
s.Append(target.DOColor(c.color, property, colorDuration).SetEase(Ease.Linear));
}
s.SetTarget(target);
return s;
}
#endregion
#endregion
#endif
#if UNITY_5_3_OR_NEWER || UNITY_2017_1_OR_NEWER
#region Unity 5.3 or Newer
#region CustomYieldInstructions
/// <summary>
/// Returns a <see cref="CustomYieldInstruction"/> that waits until the tween is killed or complete.
/// It can be used inside a coroutine as a yield.
/// <para>Example usage:</para><code>yield return myTween.WaitForCompletion(true);</code>
/// </summary>
public static CustomYieldInstruction WaitForCompletion(this Tween t, bool returnCustomYieldInstruction)
{
if (!t.active) {
if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t);
return null;
}
return new DOTweenCYInstruction.WaitForCompletion(t);
}
/// <summary>
/// Returns a <see cref="CustomYieldInstruction"/> that waits until the tween is killed or rewinded.
/// It can be used inside a coroutine as a yield.
/// <para>Example usage:</para><code>yield return myTween.WaitForRewind();</code>
/// </summary>
public static CustomYieldInstruction WaitForRewind(this Tween t, bool returnCustomYieldInstruction)
{
if (!t.active) {
if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t);
return null;
}
return new DOTweenCYInstruction.WaitForRewind(t);
}
/// <summary>
/// Returns a <see cref="CustomYieldInstruction"/> that waits until the tween is killed.
/// It can be used inside a coroutine as a yield.
/// <para>Example usage:</para><code>yield return myTween.WaitForKill();</code>
/// </summary>
public static CustomYieldInstruction WaitForKill(this Tween t, bool returnCustomYieldInstruction)
{
if (!t.active) {
if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t);
return null;
}
return new DOTweenCYInstruction.WaitForKill(t);
}
/// <summary>
/// Returns a <see cref="CustomYieldInstruction"/> that waits until the tween is killed or has gone through the given amount of loops.
/// It can be used inside a coroutine as a yield.
/// <para>Example usage:</para><code>yield return myTween.WaitForElapsedLoops(2);</code>
/// </summary>
/// <param name="elapsedLoops">Elapsed loops to wait for</param>
public static CustomYieldInstruction WaitForElapsedLoops(this Tween t, int elapsedLoops, bool returnCustomYieldInstruction)
{
if (!t.active) {
if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t);
return null;
}
return new DOTweenCYInstruction.WaitForElapsedLoops(t, elapsedLoops);
}
/// <summary>
/// Returns a <see cref="CustomYieldInstruction"/> that waits until the tween is killed or has reached the given position (loops included, delays excluded).
/// It can be used inside a coroutine as a yield.
/// <para>Example usage:</para><code>yield return myTween.WaitForPosition(2.5f);</code>
/// </summary>
/// <param name="position">Position (loops included, delays excluded) to wait for</param>
public static CustomYieldInstruction WaitForPosition(this Tween t, float position, bool returnCustomYieldInstruction)
{
if (!t.active) {
if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t);
return null;
}
return new DOTweenCYInstruction.WaitForPosition(t, position);
}
/// <summary>
/// Returns a <see cref="CustomYieldInstruction"/> that waits until the tween is killed or started
/// (meaning when the tween is set in a playing state the first time, after any eventual delay).
/// It can be used inside a coroutine as a yield.
/// <para>Example usage:</para><code>yield return myTween.WaitForStart();</code>
/// </summary>
public static CustomYieldInstruction WaitForStart(this Tween t, bool returnCustomYieldInstruction)
{
if (!t.active) {
if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t);
return null;
}
return new DOTweenCYInstruction.WaitForStart(t);
}
#endregion
#endregion
#endif
#if UNITY_2018_1_OR_NEWER
#region Unity 2018.1 or Newer
#region Material
/// <summary>Tweens a Material's named texture offset property with the given ID to the given value.
/// Also stores the material as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param>
/// <param name="propertyID">The ID of the material property to tween (also called nameID in Unity's manual)</param>
/// <param name="duration">The duration of the tween</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOOffset(this Material target, Vector2 endValue, int propertyID, float duration)
{
if (!target.HasProperty(propertyID)) {
if (Debugger.logPriority > 0) Debugger.LogMissingMaterialProperty(propertyID);
return null;
}
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.GetTextureOffset(propertyID), x => target.SetTextureOffset(propertyID, x), endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens a Material's named texture scale property with the given ID to the given value.
/// Also stores the material as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param>
/// <param name="propertyID">The ID of the material property to tween (also called nameID in Unity's manual)</param>
/// <param name="duration">The duration of the tween</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOTiling(this Material target, Vector2 endValue, int propertyID, float duration)
{
if (!target.HasProperty(propertyID)) {
if (Debugger.logPriority > 0) Debugger.LogMissingMaterialProperty(propertyID);
return null;
}
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.GetTextureScale(propertyID), x => target.SetTextureScale(propertyID, x), endValue, duration);
t.SetTarget(target);
return t;
}
#endregion
#endregion
#endif
}
// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
// ███ CLASSES █████████████████████████████████████████████████████████████████████████████████████████████████████████
// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
#if UNITY_5_3_OR_NEWER || UNITY_2017_1_OR_NEWER
public static class DOTweenCYInstruction
{
public class WaitForCompletion : CustomYieldInstruction
{
public override bool keepWaiting { get {
return t.active && !t.IsComplete();
}}
readonly Tween t;
public WaitForCompletion(Tween tween)
{
t = tween;
}
}
public class WaitForRewind : CustomYieldInstruction
{
public override bool keepWaiting { get {
return t.active && (!t.playedOnce || t.position * (t.CompletedLoops() + 1) > 0);
}}
readonly Tween t;
public WaitForRewind(Tween tween)
{
t = tween;
}
}
public class WaitForKill : CustomYieldInstruction
{
public override bool keepWaiting { get {
return t.active;
}}
readonly Tween t;
public WaitForKill(Tween tween)
{
t = tween;
}
}
public class WaitForElapsedLoops : CustomYieldInstruction
{
public override bool keepWaiting { get {
return t.active && t.CompletedLoops() < elapsedLoops;
}}
readonly Tween t;
readonly int elapsedLoops;
public WaitForElapsedLoops(Tween tween, int elapsedLoops)
{
t = tween;
this.elapsedLoops = elapsedLoops;
}
}
public class WaitForPosition : CustomYieldInstruction
{
public override bool keepWaiting { get {
return t.active && t.position * (t.CompletedLoops() + 1) < position;
}}
readonly Tween t;
readonly float position;
public WaitForPosition(Tween tween, float position)
{
t = tween;
this.position = position;
}
}
public class WaitForStart : CustomYieldInstruction
{
public override bool keepWaiting { get {
return t.active && !t.playedOnce;
}}
readonly Tween t;
public WaitForStart(Tween tween)
{
t = tween;
}
}
}
#endif
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 63c02322328255542995bd02b47b0457
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,155 @@
// Author: Daniele Giardini - http://www.demigiant.com
// Created: 2018/07/13
using System;
using System.Reflection;
using UnityEngine;
using DG.Tweening.Core;
using DG.Tweening.Plugins.Core.PathCore;
using DG.Tweening.Plugins.Options;
#pragma warning disable 1591
namespace DG.Tweening
{
/// <summary>
/// Utility functions that deal with available Modules.
/// Modules defines:
/// - DOTAUDIO
/// - DOTPHYSICS
/// - DOTPHYSICS2D
/// - DOTSPRITE
/// - DOTUI
/// Extra defines set and used for implementation of external assets:
/// - DOTWEEN_TMP ► TextMesh Pro
/// - DOTWEEN_TK2D ► 2D Toolkit
/// </summary>
public static class DOTweenModuleUtils
{
static bool _initialized;
#region Reflection
/// <summary>
/// Called via Reflection by DOTweenComponent on Awake
/// </summary>
#if UNITY_2018_1_OR_NEWER
[UnityEngine.Scripting.Preserve]
#endif
public static void Init()
{
if (_initialized) return;
_initialized = true;
DOTweenExternalCommand.SetOrientationOnPath += Physics.SetOrientationOnPath;
#if UNITY_EDITOR
#if UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_5 || UNITY_2017_1
UnityEditor.EditorApplication.playmodeStateChanged += PlaymodeStateChanged;
#else
UnityEditor.EditorApplication.playModeStateChanged += PlaymodeStateChanged;
#endif
#endif
}
#if UNITY_2018_1_OR_NEWER
#pragma warning disable
[UnityEngine.Scripting.Preserve]
// Just used to preserve methods when building, never called
static void Preserver()
{
Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
MethodInfo mi = typeof(MonoBehaviour).GetMethod("Stub");
}
#pragma warning restore
#endif
#endregion
#if UNITY_EDITOR
// Fires OnApplicationPause in DOTweenComponent even when Editor is paused (otherwise it's only fired at runtime)
#if UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_5 || UNITY_2017_1
static void PlaymodeStateChanged()
#else
static void PlaymodeStateChanged(UnityEditor.PlayModeStateChange state)
#endif
{
if (DOTween.instance == null) return;
DOTween.instance.OnApplicationPause(UnityEditor.EditorApplication.isPaused);
}
#endif
// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
// ███ INTERNAL CLASSES ████████████████████████████████████████████████████████████████████████████████████████████████
// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
public static class Physics
{
// Called via DOTweenExternalCommand callback
public static void SetOrientationOnPath(PathOptions options, Tween t, Quaternion newRot, Transform trans)
{
#if true // PHYSICS_MARKER
if (options.isRigidbody) ((Rigidbody)t.target).rotation = newRot;
else trans.rotation = newRot;
#else
trans.rotation = newRot;
#endif
}
// Returns FALSE if the DOTween's Physics2D Module is disabled, or if there's no Rigidbody2D attached
public static bool HasRigidbody2D(Component target)
{
#if true // PHYSICS2D_MARKER
return target.GetComponent<Rigidbody2D>() != null;
#else
return false;
#endif
}
#region Called via Reflection
// Called via Reflection by DOTweenPathInspector
// Returns FALSE if the DOTween's Physics Module is disabled, or if there's no rigidbody attached
#if UNITY_2018_1_OR_NEWER
[UnityEngine.Scripting.Preserve]
#endif
public static bool HasRigidbody(Component target)
{
#if true // PHYSICS_MARKER
return target.GetComponent<Rigidbody>() != null;
#else
return false;
#endif
}
// Called via Reflection by DOTweenPath
#if UNITY_2018_1_OR_NEWER
[UnityEngine.Scripting.Preserve]
#endif
public static TweenerCore<Vector3, Path, PathOptions> CreateDOTweenPathTween(
MonoBehaviour target, bool tweenRigidbody, bool isLocal, Path path, float duration, PathMode pathMode
){
TweenerCore<Vector3, Path, PathOptions> t;
#if true // PHYSICS_MARKER
Rigidbody rBody = tweenRigidbody ? target.GetComponent<Rigidbody>() : null;
if (tweenRigidbody && rBody != null) {
t = isLocal
? rBody.DOLocalPath(path, duration, pathMode)
: rBody.DOPath(path, duration, pathMode);
} else {
t = isLocal
? target.transform.DOLocalPath(path, duration, pathMode)
: target.transform.DOPath(path, duration, pathMode);
}
#else
t = isLocal
? target.transform.DOLocalPath(path, duration, pathMode)
: target.transform.DOPath(path, duration, pathMode);
#endif
return t;
}
#endregion
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7bcaf917d9cf5b84090421a5a2abe42e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,29 @@
DOTween and DOTween Pro are copyright (c) 2014-2018 Daniele Giardini - Demigiant
// IMPORTANT!!! /////////////////////////////////////////////
// Upgrading DOTween from versions older than 1.2.000 ///////
// (or DOTween Pro older than 1.0.000) //////////////////////
-------------------------------------------------------------
If you're upgrading your project from a version of DOTween older than 1.2.000 (or DOTween Pro older than 1.0.000) please follow these instructions carefully.
1) Import the new version in the same folder as the previous one, overwriting old files. A lot of errors will appear but don't worry
2) Close and reopen Unity (and your project). This is fundamental: skipping this step will cause a bloodbath
3) Open DOTween's Utility Panel (Tools > Demigiant > DOTween Utility Panel) if it doesn't open automatically, then press "Setup DOTween...": this will run the upgrade setup
4) From the Add/Remove Modules panel that opens, activate/deactivate Modules for Unity systems and for external assets (Pro version only)
// GET STARTED //////////////////////////////////////////////
- After importing a new DOTween update, select DOTween's Utility Panel from the "Tools/Demigiant" menu (if it doesn't open automatically) and press the "Setup DOTween..." button to activate/deactivate Modules. You can also access a Preferences Tab from there to choose default settings for DOTween.
- In your code, add "using DG.Tweening" to each class where you want to use DOTween.
- You're ready to tween. Check out the links below for full documentation and license info.
// LINKS ///////////////////////////////////////////////////////
DOTween website (documentation, examples, etc): http://dotween.demigiant.com
DOTween license: http://dotween.demigiant.com/license.php
DOTween repository (Google Code): https://code.google.com/p/dotween/
Demigiant website (documentation, examples, etc): http://www.demigiant.com
// NOTES //////////////////////////////////////////////////////
- DOTween's Utility Panel can be found under "Tools > Demigiant > DOTween Utility Panel" and also contains other useful options, plus a tab to set DOTween's preferences
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: fccfc62abf2eb0a4db614853430894fd
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 822284e79d8689848bdc8a2043cdf75c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+487
View File
@@ -0,0 +1,487 @@
v2.1.15 2020年6月24日
新增特性
1、生成代码过滤器
2、优化反射查找delegate匹配bridge的性能
3、unity 2019.2以上版本手机版本注入不了的问题
变更
bug修复
1、反射查找同名delegate桥接在不生成代码的时候表现不一致
2、嵌套struct标注为PackAsTable时生成代码报错
3、反射wrap代码加入栈空间检查
4、如果枚举定义了很多个值(几千个),会触发unity在android下的一个bug:函数体很大而且有很多分支,执行该函数会crash
5、chunkname和脚本文件名不一致的问题
6、最小生成模式枚举生成代码报错
7、当采用反射方式注册枚举值时,如果一个枚举有多个相同的值,比如A,B都是1,那么在lua里头访问B将会为空
8、sbyte[]在.net 4下push到lua变成字符串的问题
9、泛型导致生成代码失败的问题
10、非Assembly-CSharp程序集注入时,out参数处理有误
11、内嵌类通过xlua.private_accessible设置私有访问可能失败的问题
12、cecil插入指令后,并未自动更新offset,某种情况下会导致计算偏移量错误
v2.1.14 2019年2月27日
新增特性
1、新增nintento switch的支持
2、unity 2018兼容
3、android arm64支持
4、原生库的visual studio 2017编译支持
5、增加“XLua/Generate Minimize Code”菜单
6、防止有的工程有非法的dll导致生成代码中断
7、更高效的lua_pushstring(需要通过NATIVE_LUA_PUSHSTRING开启)
变更
1、window库默认编译器改为visual studio 2017
bug修复
1、修正枚举类型如果只加GCOptimize不加LuaCallCSharp会crash的问题
2、示例配置加入对Edtitor类的过滤
3、UWP兼容修复
4、接口继承引入的同签名方法实现
5、未生成代码,extension方法行为不一致
6、修复Nullable类型参数,如果最后一个参数是nil,会导致其他参数全是nil的问题
v2.1.13 2018年12月5日
新增特性
1、新增AdaptByDelegate注入模式;
2、新增xlua.get_generic_method,用于调用泛型函数;
3、支持类似CS.System.Collections.Generic.List(CS.System.Int32)的泛型写法;
4、注入新选项:忽略编译器自动生成代码,以及不生成base代理;
5、针对lua编程以及热补丁,均添加直接可用的自动化配置样例;
6、新增luajit的gc64支持;
7、加入兼容字节码(一份字节码支持32位和64位系统)的支持;
8、内置新lua内存泄漏检测工具;
9、delegate桥接动态实例化:delegate是4个参数以内,参数均引用类型,无返回值或者返回引用类型,不用配置CSharpCallLua也能调用lua函数;
10、提供util.print_func_ref_by_csharp函数,用于查看当前被C#引用的lua函数;
11、支持无CS全局变量的工作方式;
变更
1、虚拟机升级:lua5.3.4 -> lua5.3.5luajit2.1b2 -> luajit2.1b3
2、delegate bridge代码段占用优化;
3、改为PostProcessBuild事件检查是否生成代码;
4、适配xcode 10osx平台不再支持32bit版本构建;
5、名字空间、类名拼写错误时,对静态成员的设置会报错;
6、防止CS全局table被删除导致xlua工作异常;
7、Windows下构建lib,若使用vs 2015参数执行cmake失败,则继续尝试使用vs 2017;
8、编辑器下不生成代码时,也检查Blacklist,维持和运行时一致;
bug修复
1、泛型的数组生成代码报错;
2、防止对TypeExtensions配置了LuaCallCSharp后,lua里头IsValueType之类的判断永真;
3、生成代码过滤掉含指针的函数和字段;
4、适应索引器属性名不是Item的情况;
5、解决attribute初始化异常会导致生成代码,注入终止的问题;
6、精简模式下空Enum生成代码错误;
7、通过把初始化函数分割成小函数,规避unity在android下执行大函数crash的bug
8、Assignable处理obj为null情况;
9、内嵌类不Obsolete,但外层类Obsolete的生成代码报错
10、解决inline注入方式下,如果lua逻辑跑异常,看不到异常信息的问题;
11、修复xlua.private_accessible访问后,同名public的方法无法访问的Bug;
12、[Out]修饰的参数不应该生成out关键字;
13、通过反射查找合适的适配器时,有可能访问到非适配器函数;
14、精简模式导出代码无get_Item、set_Item
15、IntKey方式下不自动xlua.private_accessible的问题;
v2.1.12 2018年7月9日
新增特性
1、Nullable的支持
2、支持Assembly-CSharp之外的dll注入(beta
3、执行xlua.hotfix,会自动让该类private能访问
4、xlua.private_accessible优化:1、会把基类的也设置能私有访问;2、延迟到第一次访问类才私有化
5、新增xlua.util.state,可为一个c#对象新增状态
6、this[string field]或者this[object field]操作符重载新增get_Item和set_Item调用
7、正在编译时注入打印error信息
8、interface配置到CSharpCallLua时的事件跟索引映射的自动实现
9、unity5.5以上去掉WARNING: The runtime version supported by this application is unavailable打印
变更
1、去除Stateful方式(因为xlua.util.state已经可以达成类似的效果)
2、废弃掉内嵌模式模式
bug修复
1、生成代码局部变量加下划线,防止符号冲突
2、如果类没放到Hotfix列表,不生成base调用代理
3、代码重构,可读性优化
4、解决带params byte[]可能会导致生成代码编译错误的问题
5、解决类含有private event的时候,无法xlua.private_accessible的问题
6、构造函数注入,如果branch外紧跟Ret指令,注入逻辑应该在branch以及Ret之间
7、构造函数注入,如果注入指令后导致跳转范围大于一个字节,应修改为长跳转
8、解决一个delegate如果不是某个类的内嵌类型时,CS.namespace.classname为空的问题
9、防止Editor下的Util类名字冲突
10、泛型override有异常,先过滤掉
11、解决空enum导致生成代码编译错误
12、解决uwp平台下il2cpp方式打包无法访问任何类的问题
13、hotfix一个私有类型的params参数的函数,导致生成代码编译错误、注入失败的问题
14、如果两个LuaBase指向的是同一个Lua对象,GetHashCode应该返回的是同一个值
15、[Out]标记参数生成代码编译失败
16、交错数组+多维数组的复合,生成代码报错的问题
v2.1.11 2018年3月20日
新增特性
1、xlua.private_accessible支持私有内嵌类型
2、添加xlua.release,用于主动解除lua对c#某对象的引用
3、支持内嵌委托的显示构造
4、需要传class的地方(比如xlua.private_accessible),支持传C#的Type对象
5、支持用pairs遍历IEnumerable对象
6、热补丁场景下,支持override函数调用被override函数(对应c# base关键字)
变更
1、简化property的反射访问,简化后有更好的兼容性;
bug修复
1、ios 11兼容(去除system调用)
2、实现了interface的struct不走gc优化代码的问题
3、emit特性的.net兼容性
4、emit对于ulong的const值处理不当
5、interface桥接代码,interface继承时,父interface和子interface有同名不同类型属性时的生成代码报错
6、多虚拟机下,不断创建和销毁协程时,可能出现协程指针重复
7、当参数为泛型类型时,如ICollectio时,不应该生成代码
v2.1.10 2017年9月18日
新增特性
1、新增DoNotGen配置,支持一个类型部分函数用反射,部分用生成;
2、新增wrapper的emit
3、webgl支持;
4、lua实现interface支持interface继承;
5、window下支持android编译(由xdestiny110提供);
6、打包时,如果没执行过“Generate Code”将报错;
变更
1、 async_to_sync的改为resume错误时报错;
2、il2cpp下,暂时去掉泛型的反射调用;
3、升级到lua5.3.4并合入2017-9-1为止所有官方patch
bug修复
1、C#仅声明delegate和MulticastDelegate,通过反射创建lua function映射时crash
2、解决一些古老版本window(比如xp)的dll兼容问题;
v2.1.9 2017年8月10日
新增特性
1、新增最小生成模式(通过GEN_CODE_MINIMIZE切换),可以节省50%的text段空间;
2、新增xlua.util.createdelegate,支持在lua直接用C#函数创建delegate而不需要通过lua适配;
3、xlua.private_accessible支持public int Prop { get; private set; }
4、新增 xlua.getmetatable、xlua.setmetatable、xlua.setclass、xlua.genaccessor,用以支持lua使用C#类型直接在lua侧完成;
5、反射下扩展方法的支持;
6、lua53版本支持位操作符重载:C#侧的位操作符重载对应到lua的位操作符重载;enum全部加上&和|位操作符;
工程优化
1、加入travis持续集成;
变更
1、LuaCallCSharp自动去除匿名类型;
2、THREAD_SAFT改为THREAD_SAFE
3、GenFlag.GCOptimize标记为过时;
4、删除过时的GenConfig配置方式;
bug修复
1、window phone下一些系统api是禁用的,源码中去掉;
2、泛型约束是struct的时候,生成代码失败;
3、unity2017 .net 4.6,枚举生成代码报错;
v2.1.8 2017年6月27日
新增特性
1、Hotfix标签添加几个订制参数:ValueTypeBoxing、IgnoreProperty、IgnoreNotPublic、Inline、IntKey
2、Hotfix代码注入优化,减少text段占用;
3、Hotfix配置支持放Editor目录,可以减少text段占用;
4、支持以指定类型传递object参数;
5、反射调用Obsolete方法在Editor下打印warning
变更
bug修复
1、pinvoke独立设置的In,Out属性可能导致生成代码失败;
2、如果业务在全局名字空间有和xLua名字空间的同名类,生成代码编译失败;
v2.1.7 2017年5月17日
新增特性
1、支持发布UWP(含HoloLensXbox oneWin10 Mobile、Win10 PC)应用;
2、支持对lua源代码ras+sha1签名;
3、如果没安装Tools提示“please install the Tools”;
4、linxu版本的支持;
5、支持bitcode打包;
6、对所有struct新增无参数构造函数;
7、delegate的参数名改为p0到pn,防止hotfix时业务代码变量和生成代码冲突;
8、支持对成员名为C#关键字的情况;
9、新增util.loadpackage,和require类似,通过searcher加载文件,不同的是,它不执行,而且也不会cache到package.loaded
10、优化模版引擎大文件的生成性能;
11、新增不需要生成代码的注入方式;
12、支持构造函数参数带ref和out修饰符;
13、构造函数也支持黑名单排除;
变更
1、this[object field]操作符重载;
2、反射的数据转换规则改成和生成代码一致;
3、忽略掉匿名类及匿名函数的注入;
bug修复
1、规避Unity的bugList<CustomType>CustomType是当前执行程序集的类型,这在.Net是不需要指明程序集就可以通过Type.GetType得到,但Unity下不行。
2、解决反射下,可变参数不提供时,传null的问题;
3、继承了另外一个程序集的类型,使用了protected类型会导致注入失败;
4、luajit去掉dlopen和dlsym的调用;
5、解决通用版本的生成代码工具找不到模版的问题;
6、修复通用版本反射导入泛化类型的问题;
7、反射调用含delegate参数的的api,会因为缓存而导致调用LuaEnv.Dispose失败;
8、兼容老版本的C编译器,声明要放开头;
9、生成代码对hotfix的检测算法和注入工具不一致导致的注入失败;
10、注入的nested类型是public,但其的外层类型非public,生成代码报错;
11、析构函数只判断名字可能出现误判;
12、构造函数是非public的,可能会导致找不到适配delegate而注入失败;
13、修正Extension method会在所有子类都生成代码的bug(2.1.6泛化特性引入);
14、构造函数重载,只有一个能hotfix成功;
15、规避一个可能是il2cpp的bug(unity5.4):字符串参数默认值是""ios下在反射的default value也是Reflection.Missing
16、将一个table传到List<>,取了最后一个参数,而不是那个table的长度;
17、ldarg指令在这种场景下il2cpp转换时会出现异常:1、采用模版注入;2、从4到255间有一个输出参数;改为兼容性更好的ldarg.s;
18、解决配置了System.Delegate到CSCallLua,执行生成代码会编辑器会crash的问题;
19、扩展函数可能和原来的函数同名,反射实现并未考虑到这种情况;
20、通用版本的可变参数delegate调用异常;
21、unity4规避lua53冲突的方式改为返回null更合适,异常方式会导致IsNull无法正常工作;
22、lua_tostring解码失败改为UTF8解码;
v2.1.6 2017年3月1日
新增特性
1、带约束的泛型支持(by forsakenyang);
2、非Unity的.net环境支持;
3、代码注入支持小工具方式,该方式不用拷贝cecil库,可以解决拷错cecil库版本或者和Unity,VS插件冲突的问题;
4、Hotfix配置支持字段和属性
5、更方便的Unity协程hotfix
6、在hotfix触发事件;
7、LuaTable添加ForEach方法以及Length属性;
8、cmake生成项目优化:保留源文件目录结构;
9、对已经Dispose的LuaEnv的访问做保护;Dispose时检查callback是否已经都释放,没释放的话报错;
10、支持释放Hotfix回调;
变更
1、构造函数改为执行原有逻辑后调用lua;
2、this[string field]操作符重载会影响到继承调用,去掉该特性的支持;
3、编辑器下的代码注入改为手动方式;
bug修复
1、防止定义了同时定义get_xx方法以及xx属性的生成代码的重名。
2、struct注入代码无效;
3、Utils加名字空间,防止和业务冲突;
4、返回定长多维数组的delegate,生成代码可能会冲突;
5、interface,以及编辑器下不生成代码情况下,对可变参数的展开;
6、il2cpp下,如果不生成代码,会报ManifestModule不支持;
7、规避Unity4的bug:访问一个已经被Distroy的UnityEngine.Object,编辑器下会崩溃,这个问题在Unity5,或者luajit版本都不会出现;
8、修改上个版本引入的问题:xlua_setglobal会漏一个值在栈上,这会导致一些32位应用不稳定;
9、当delegate参数只有ref和out的区别的话,报重载冲突;
v2.1.5 2017年1月13日
新增特性
1、全平台热补丁;
2、新增线程安全模式,可通过THREAD_SAFT宏打开;
3、新增更简便的配置方式,具体参见XLua\Doc下《XLua的配置.doc》;
4、多虚拟机实例时的自动Dispose;
5、内存优化:减少匿名闭包到delegate映射的内存占用;减少LuaFunction以及LuaTable内存占用;减少lua table映射C#interface的gc
6、生成代码速度优化;
7、支持直接在lua侧clone C#结构体;
8、LuaFunction新增无gc调用api
变更
1、delegate必须都加[CSharpCallLua]才支持C#到lua的回调(以前参数和返回值都相同的delegate只要其中一个加了就可以);
2、加回string/number到枚举的自动转换;
bug修复
1、枚举不生成代码时,第一次使用会产生两个不同的userdata;
2、数组和System.Type的相互引用导致System.Type生成代码无法加载;
3、更安全的异常处理,封装lua_setglobal,lua_getglobal的异常,C#回调保证所有C#异常都catch并转换到成lua error。
v2.1.4 2016年11月29日
新增特性
1、加了ReflectionUse会自动生成到link.xml,可以防止il2cpp下因stripping导致的反射不可用;
2、开放生成引擎,可二次开发自己生成插件,生成所需的代码或配置;
3、GetInPath和SetInPath无C# gc优化;
4、一个lua table自动转换为带GCOptimize标签的复杂类型以及该复杂类型的一维数组不使用反射,如果这复杂类型是纯值类型,无c# gc;
变更
1、基于一致性以及性能的考虑,不支持数字和字符串到枚举的静默转换,须主动调用起类下的__CastFrom;
2、名字空间从LuaInterface改为XLua
3、LuaTable的几个可能导致gc的api标注为Obsolete
4、在不指明返回类型的情况下,如果一个number是整数会优先转换成整数;
bug修复
1、含能隐式转换intlongdecimal的类型传到lua变成decimal
2、反射的重载判断,如果可变参数的位置上是一个不匹配的参数,也会判断为匹配成功;
3、可变参数+重载的话,可变部分不传会报无效参数;
4、加了LuaCallCSharp的Extension method,在Editor下不生成代码不可用;
v2.1.3 2016年11月09日
新增特性
1、LuaTable新增Get<TKey, TValue>和Set<TKey, TValue>接口,table操作支持值类型无gc;
2、支持decimal,不丢失精度而且传递到lua无gc;
3、增加LuaEnv.LoadString<T>接口,用于指定返回的delegate类型;
4、例子刷新:新增Helloworld,无GC调用,Lua面向对象,协程例子;
5、enum优化:传递到lua无gc,从int或者string到枚举转换无gc
6、event的+/-优化:性能提升一倍,而且无gc;
7、生成代码简化;
变更
1、uint在lua53映射到lua_Integer
2、StreamingAssets加载改为优先级最低;
bug修复
1、生成代码下,如果LuaTable或者LuaFunction参数为null会抛异常;
2、lua5.3下,浮点到枚举的静默转换失败;
3、反射下struct类型参数带默认值抛异常;
4、lua53下Length返回浮点;
v2.1.2 2016年10月08日
新增特性
1、支持lua5.3,进而支持苹果bitcode,原生64位整数,位运算,utf8等特性;
2、CMake编译,更方便加入第三方插件
3、数组性能优化,包括访问性能以及gc
4、C#调用lua函数减少一次lua gc;
5、优化启动时间;
6、减少类型加载的gc
7、优化ObjectPool的内存占用;
8、优化小字符串传入lua的gc
9、LuaTable添加Cast接口,用于LuaTable到其它类型的转换,比如interface;
10、LuaFunction添加Cast接口,用于LuaFunction到delegate的转换;
变更
1、lua内部只有带符号的64整数类型,并增加无符号数库
2、如果不想对Extension Method生成代码,又希望在反射下用,需要添加ReflectionUse
bug修复
1、对ObjectPool已经Destroy的UnityEngine.Object的引用自动解除功能的内存泄漏问题;
2、规避某些版本(已知是5.3.3)的Unity的bug导致的内存泄漏问题;
3、LuaTable或者LuaFunction做返回值的delegate生成代码可能报错;
v2.1.1 2016年08月29日
新增特性
1、支持编辑器下不用生成代码能运行;
2、新增IntPtr的支持
3、增加对ObjectPool已经Destroy的UnityEngine.Object的引用自动解除;
4、在LuaEnv添加对lua_gc一些封装;
bug修复
1、生成代码传送一个LuaFunction、LuaTable到lua和反射版本不一致,生成代码传送过去是一个C#对象,而反射是Lua函数、table对象,反射的处理更合适;
2、修复同名的静态以及成员方法冲突的问题;
3、修复对interface生成CSharpCallLua代码时,interface含indexer时的报错;
4、修复Editor在运行后会new一个xlua实例的bug;
5、修复通过生成代码调用同时含可变参数和默认值的函数,如果不传参数,将会出错的bug;
6、修复调试时,找不到socket库的bug;
变更
1、反射不做重载方法顺序调整,顺序改为固定且生成代码保持一致;
2、i64加上fade_id,参数传递时更安全;
3、重新加入tdr的from_file的支持;
v2.1.0 2016年08月08日
新增特性
1、满足条件struct传递到lua无gc,struct需要满足什么条件才能被优化呢?
a. struct允许嵌套其它struct,但它以及它嵌套的struct只能包含这几种基本类型:byte、sbyte、short、ushort、int、uint、long、ulong、float、double
b. struct本身以及使用到该struct的地方需要加LuaCallCSharp,并且加了GCOptimize设置;
2、全新实现的反射机制,更容易和生成代码配合使用
a. 支持extension methodsEnum.__CastFrom
b. ios下支持反射使用event
c. 对类型映射、可变参数调用调整为和生成代码一致;
d. 性能更好,gc更少;
3、生成代码菜单简化,并增加“Generate Minimum”选项;
4、支持生成代码配置文件放Editor目录;
变更
1、luajit统一升级成2.1.0b2
2、luasocket库改为按需加载;
3、重载的stringbyte[]参数检查允许为nil
4、子类访问不触发父类加载;
5、struct的ref参数的修改会修改lua测该参数的值;
6、生成代码加载改为静态(原来是反射);
7、菜单改为更简洁;
8、tdr改为默认不加载;
9、StreamingAssets加载lua改为废弃特性;
bug修复
1、参数或者返回值是泛型类的数组,或者是二维数组,生成代码报编译错误;
2、抽象类生成代码报编译错误;
3、消除Clear生成代码的warning
4、profiler、i64库不支持多实例;
v2.0.5 2016年05月18日
新增特性
1、util.async_to_sync,可以更好的利用lua的协程实现同步编程、异步执行;或者异步等待www等;
2、生成代码的规范度调整,消除一些工具的告警;
bug修复
1、解决在lua gc移除weak table和调用__gc的时间窗内push同一对象,会生成指向同一C#对象的不同userdata的问题;
2、上版本的的lua内存工具并未打包;
3、修正嵌套类型不能生成代码的问题;
v2.0.4 2016年05月04日
新增特性
1、新增函数调用时长报告功能;
2、新增lua内存泄漏定位工具;
3、lua测加入对64位无符号数的支持;
变更
1、支持多种delegate绑定到一个clousre。调整之前一个clousre只能对应一种delegate
bug修复
1、tdr处理长度为1的数组的错误(本来解包应该是{[1] = {a = 1}}的,却是{{a=1}});
2、tdr数值处理错误(int的-1会解成一个很大的正数)
v2.0.3 2016年04月13日
新功能
1、添加“Advanced Gen”功能,用户可以自定义生成代码的范围;
2、支持对库生成Static pusher
变更
1、LuaTable以及InterfaceBirdage改为触发metatable
2、Extension Methods不自动加到被扩展类,需要加入生成列表;
3、移除特殊ValueType优化;
bug修复
1、Extension Methods为私有时,生成代码语法错误;
2、重载函数含ulong时,生成代码语法错误;
3、反射调用时的默认值处理错误;
4、C#向lua传中文字符的长度处理错误;
v2.0.2 2016年04月06日
变更
1、库的生成代码配置支持多份,方便项目的模块化;
2、enum的生成代码合并到一个文件里头;
3、优化异常处理;
4、发布包把库和教程、例子分离,更干净;
5、小bug修改;
升级指引
由于文件有点变动,直接覆盖原有lib会报错,需要:
1、删除原来的XLua目录;
2、解压xlua_v2.0.2.zip到Assets下;
3、重新执行代码生成;
v2.0.1 2016年03月24日
1、支持C# 的extension methods
2、lua调试方面的支持;
3、android下require一个不存在的lua文件可能成功的bug;
4、TDR 4 Lua库的更新;
5、多机型的兼容性测试;
v2.0.0 2016年03月08日
1、性能优化,性能对比报告请看主页;
2、加入官方lua版本的tdr
3、支持64位整数;
4、修正lua中对C#异常pcall引发的不稳定;
5、易用性的优化;
6、其它一些bug的修改。
1.0.2 2015年12月09日
1、解决新版本(已知5.2版本)下,streamAssetsPath不允许在构造函数访问导致的bug;
2、新增windows x64版本的支持;
3、对web版本才用到的代码加入条件编译,减少对手机版发布包的影响;
4、生成代码文件名去掉“+”号;
5、删除4.6的生成代码,以免在新版本报引用过时api的错;
v1.0.1 2015年11月30日
1、支持pcall捕捉C#异常;
2、新增cast方法,支持这种场景:实现类是internal声明,只提供interface
3、解决interface下如果有event,生成代码编译报错的bug;
4、解决interface下有Obsolete的方法,字段,生成代码编译报错的bug;
5、解决含private的默认geter/setter生成代码编译报错的bug
6、修正类在全局空间下生成代码不可用的bug;
7、修正bridge代码返回值处理错误。
v1.0.0 2015年03月30日
第一个版本
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: be3fe4ee249c5274693e7b6f8053e861
timeCreated: 1470364015
licenseType: Pro
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:
+5
View File
@@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: 67edfc4b640373846b14362bf8769576
folderAsset: yes
DefaultImporter:
userData:
@@ -0,0 +1,128 @@
## What & Why
XLua's currently built-in extension libraries:
* LuaJIT support for 64-bit integers;
* Positioning tool for function call times and memory leaks;
* LuaSocket library for supporting ZeroBraneStudio;
* tdr 4 lua;
With the increasing extensiveness and intensiveness of project use, the current extension libraries have been unable to meet the project team needs. Since various projects require very different extension libraries, and since mobile phone platforms are sensitive to the size of the installation package, xLua is unable to meet these needs through pre-integration. That is why we are offering this tutorial.
In this tutorial, we will use lua-rapidjson as an example to explain step by step how to add C/C++ extensions to xLua. Once you know how to add them, you will also know how to delete them naturally. The project team can delete those pre-integrated extensions if they are not used any more.
## How it is done
There are three steps:
1. Modify the build file and project settings. Compile the extensions you want to integrate into the XLua Plugin directory.
2. Call the C# APIs on xLua so that the extensions can be loaded as needed (when required in the Lua code).
3. (Optional) If you need to use 64-bit integers in your extensions, you can use xLua's 64-bit extension library to work with C#.
### First, add extensions & compile.
Preparations
1. Extract the xLuas C source code package to the same level directory as the Assets of your Unity project.
Download the lua-rapidjson code and place it anywhere you like. In this tutorial, we place the rapidjson header file in the $UnityProj\build\lua-rapidjson\include directory, and place the extended source code rapidjson.cpp in the $UnityProj\build\lua-rapidjson\source directory (Note: $UnityProj refers to your project directory).
2. Add extensions to CMakeLists.txt
xLuas platform Plugins are compiled using CMake. The advantage of this is that the compilations of all platforms are written in a makefile, and most compilation processing logic is cross-platform.
XLua's CMakeLists.txt provides extension points (all lists) for third-party extensions:
1. THIRDPART_INC: Third-party extension header search path.
2. THIRDPART_SRC: Third-party extended source code.
3. THIRDPART_LIB: The library on which third-party extensions rely.
The following is added with RapidJSON:
#begin lua-rapidjson
set (RAPIDJSON_SRC lua-rapidjson/source/rapidjson.cpp)
set_property(
SOURCE ${RAPIDJSON_SRC}
APPEND
PROPERTY COMPILE_DEFINITIONS
LUA_LIB
)
list(APPEND THIRDPART_INC lua-rapidjson/include)
set (THIRDPART_SRC ${THIRDPART_SRC} ${RAPIDJSON_SRC})
#end lua-rapidjson
See the attachment for the complete code.
3. Compile platforms
All compiled scripts are named with this format: make_platform_lua version.extension name.
For example, the name of the Windows 64-bit Lua 5.3 version is make_win64_lua53.bat, and the name of the Android LuaJIT version is make_android_luajit.sh. you can execute the corresponding script to compile the target version.
The compiled scripts are automatically copied to the plugin_lua53 or plugin_luajit directory. The former is for Lua 5.3 and the latter for LuaJIT.
The supporting Android script is used on Linux. The NDK path at the beginning of the script must be modified accordingly.
### Second, C# side integration:
Each C extension library on Lua will provide a function, luaopen_xxx, where xxx is the name of the dynamic library. For example, the function for the Lua-RapidJSON library is luaopen_rapidjson. Such functions are automatically called by the Lua virtual machine when loading the dynamic library. In the mobile platform, we cannot load the dynamic library due to iOS restrictions. They are compiled directly into the process instead.
For this purpose, xLua provides an API to replace this feature (LuaEnv's member methods):
public void AddBuildin(string name, LuaCSFunction initer)
Parameters:
Name: name of the buildin module, a parameter entered during require;
initer: the initialization function; Its prototype is public delegate int lua_CSFunction(IntPtr L); This must be a static function and be modified with the property MonoPInvokeCallbackProperty; This API will check these two conditions.
We use calling luaopen_rapidjson to show how to use it.
Extend the LuaDLL.Lua type, export luaopen_rapidjson to C# via pinvoke, and then write a static function that satisfies the definition of lua_CSFunction. You can write initialization work in it, such as calling luaopen_rapidjson. Here is the complete code:
namespace LuaDLL
{
public partial class Lua
{
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int luaopen_rapidjson(System.IntPtr L);
[MonoPInvokeCallback(typeof(LuaDLL.lua_CSFunction))]
public static int LoadRapidJson(System.IntPtr L)
{
return luaopen_rapidjson(L);
}
}
}
Then call AddBuildin:
luaenv.AddBuildin("rapidjson", LuaDLL.Lua.LoadRapidJson);
After this, it should work properly. Try the extension in the Lua code:
local rapidjson = require('rapidjson')
local t = rapidjson.decode('{"a":123}')
print(t.a)
t.a = 456
local s = rapidjson.encode(t)
print('json', s)
### Third, 64-bit transformation
Include the i64lib.h file in a file that requires a 64-bit transformation.
The header file include these APIs:
//Place an int64 on stack/uint64
void lua_pushint64(lua_State* L, int64_t n);
void lua_pushuint64(lua_State* L, uint64_t n);
//Judge whether int64 is at the pos position on stack/uint64
int lua_isint64(lua_State* L, int pos);
int lua_isuint64(lua_State* L, int pos);
//Get an int64 from the pos position on stack/uint64
int64_t lua_toint64(lua_State* L, int pos);
uint64_t lua_touint64(lua_State* L, int pos);
The usage of these APIs varies depending on the actual situation. See the attached file (rapidjson.cpp file).
Compile project related modifications
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0ae08314c9c889249bbd484254109060
timeCreated: 1529661499
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,135 @@
# XLua configuration
All xLua configurations support three methods: tagging, static lists, and dynamic lists.
There are two requirements and two recommended items for configuration:
* List mode must use static fields/properties.
* List mode must be placed in a static type.
* Using tagging is not recommended.
* Placing the list mode configuration in the Editor directory is recommended.
**Tagging**
xLua uses a whitelist to indicate which code is to be generated, and the whitelist is configured via attributes. For example, if you want to call a C# type from Lua or you want to generate the adaptation code, you can add a LuaCallCSharp tag for this type:
~~~csharp
[LuaCallCSharp]
publicclassA
{
}
~~~
This mode is convenient, but it will increase the code on the il2cpp and therefore is not recommended.
**Static list**
Sometimes we cannot directly tag a type, such as a system API, a library without source code, or an instantiated generic type. In this case, you can declare a static field in a static type. This field can be any type except for BlackList and AdditionalProperties, as long as IEnumerable&lt;Type&gt; is implemented (these two exceptions will be specifically described later). Then add a tag to this field:
~~~csharp
[LuaCallCSharp]
public static List<Type> mymodule_lua_call_cs_list = new List<Type>()
{
typeof(GameObject),
typeof(Dictionary<string, int>),
};
~~~
This field needs to be placed in a **static type** and placing it in the **Editor directory** is recommended.
**Dynamic list**
Declare a static property and tag it accordingly.
~~~csharp
[Hotfix]
public static List<Type> by_property
{
get
{
return (from type in Assembly.Load("Assembly-CSharp").GetTypes()
where type.Namespace == "XXXX"
select type).ToList();
}
}
~~~
Getter is code. You can use it to implement a lot of results, such as configuration by namespace, configuration by assembly, and so on.
This property needs to be placed in a **static type** and placing it in the **Editor directory** is recommended.
### XLua.LuaCallCSharp
When adding this configuration for a C# type, xLua will generate the adapter code for this type (including constructing an instance for the type, and accessing its member properties & methods and static properties & methods). Otherwise, it will try to gain access using the reflection mode with lower performance.
Adding this configuration to the Extension Methods of a type will also generate the adaptation code and append it to the member methods of the extended type.
XLua will only generate the type loaded with this configuration. It will not automatically generate the adaptation code of its parent type. When accessing the parent type method of the child type object, if the parent type has the LuaCallCSharp configuration, the parent type's adaptation code will be executed. Otherwise it will try to gain access using the reflection mode.
The reflection mode access not only has poor performance, but also may cause failed access on the il2cpp due to code stripping. This problem can be avoided through the ReflectionUse tag, which is described below.
### XLua.ReflectionUse
When adding this configuration to a C# type, xLua generates a link.xml to block code stripping on the il2cpp.
For extension methods, you must add LuaCallCSharp or ReflectionUse to make them accessible.
It is recommended that all types to be accessed in Lua have the LuaCallCSharp or ReflectionUse tag, to insure their proper operation on all platforms.
### XLua.DoNotGen
This indicates that some of the functions, fields, and properties in a type do not generate code and are accessed through the reflection mode.
Only the fields or properties in the standard Dictionary<Type, List<string>> can be used. The key indicates the effective type. Value is a list. The name of the functions, fields, and properties with no code generated are configured.
The differences from ReflectionUse are: 1. ReflectionUse specifies the entire type; 2. Upon the first access to a function (field, property), ReflectionUse will wrap the entire type, while DoNotGen will only wrap the function (field, property). In other words, DoNotGen is lazier.
The differences from BlackList are: 1. BlackList cannot be used when it is configured. 2. BlackList can specify an overloaded function, while DoNotGen cannot.
### XLua.CSharpCallLua
This allows you to adapt a Lua function to a C# delegate (one scenario is various callbacks at the C# side: UI events, delegate parameters, such as List&lt;T&gt;:ForEach; another scenario is to use the Get function of LuaTable to indicate that a Lua function is bound to a delegate), or to adapt a Lua table to a C# interface. The delegate or interface needs this configuration.
### XLua.GCOptimize
A C# pure value type (Note: It refers to a struct that contains only the value type, and it can nest other structs that contain only the value type) or a C# enumerated value has this configuration. xLua generates gc-optimized code for this type. The result is that the value type is passed between Lua and C# with no (C#)gc alloc generated, and that no gc is generated during array access to this type. For various GC-free scenarios, refer to the 05\_NoGc example.
Any type except enumeration (including the complex types that contain parameterless constructors) will generate Lua tables for that type, as well as the conversion code of a one-dimensional array with modified type. This will optimize the performance of this conversion, including fewer gc allocs.
### XLua.AdditionalProperties
This is GCOptimize's extended configuration. Sometimes, some structs want to make the field private and access the field through the property. In this case, you need to use this configuration (by default, GCOptimize only packetizes/depacketizes the public field).
The tagging mode is relatively simple and the configuration mode is complicated. The requirements are that Dictionary&lt;Type, List&lt;string&gt;&gt; type, and the Key of the Dictionary are effective types; and value is the list of property names. See xLua's configuration of several UnityEngine value types and the SysGCOptimize type.
### XLua.BlackList
If you do not want to generate an adaption code for a member of a type, you can implement it with this configuration.
The tagging method is relatively simple, and the corresponding member can be added.
Considering that it may be necessary to add one of the overloaded functions to the blacklist, the configuration is more complicated. The type is List&lt;List&lt;string&gt;&gt;. For each member, the first-level list has only one entry and the second-level list is a string list. The first string is the full path name of the type, the second string is the member name. If the member is a method, you also need to list the full path of the type of its parameters starting from the third string.
For example, the following adds a property of GameObject and a method of FileInfo to the blacklist:
~~~csharp
[BlackList]
public static List<List<string>> BlackList = new List<List<string>>() {
new List<string>(){"UnityEngine.GameObject", "networkView"},
//new List<string>(){ typeof(UnityEngine.GameObject).FullName, "networkView"},
new List<string>(){"System.IO.FileInfo", "GetAccessControl", "System.Security.AccessControl.AccessControlSections"},
//new List<string>(){ typeof(System.IO.FileInfo).FullName, "GetAccessControl",typeof(System.Security.AccessControl.AccessControlSections).FullName },
};
~~~
### The following is the generator configuration, which must be placed in the Editor directory.
### CSObjectWrapEditor.GenPath
Configures the path of the generated code, with the type being a string. By default, it is plated in &quot;Assets/XLua/Gen/&quot;.
### CSObjectWrapEditor.GenCodeMenu
This configuration is used for secondary development of the build engine. When adding this tag to a parameterless function, it will trigger calling the function when executing the &quot;XLua/Generate Code&quot; menu.
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 198070d8475ff3043b6c72a906feebee
timeCreated: 1529661499
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,109 @@
## Secondary development of the build engine
xLua's build engine supports secondary development, and you can use it to generate some text files (for example, code and configuration files). The xLua's link.xml file is generated by the build engine plugin. Other application scenarios (such as generating a Lua IDE auto configuration file) can also be accomplished using this feature.
## Overview
The plugin needs to provide two things: 1. a template for generated files, 2. a callback function that accepts the user configuration and returns the data that needs to be injected into the template and the output stream of the files.
## Template syntax
The template syntax is simple, with only three elements:
* eval: The syntax is <%=exp%>. Exp is an arbitrary expression that will calculate and output the value of exp as a string.
* Code: The syntax is <% if true then end%>. The blue part is any Lua code that will be executed.
* Literal: This contains the parts other than eval and code. Literal means output as it is.
Example:
~~~xml
<%
require "TemplateCommon"
%>
<linker>
<%ForEachCsList(assembly_infos, function(assembly_info)%>
<assembly fullname="<%=assembly_info.FullName%>">
<%ForEachCsList(assembly_info.Types, function(type)
%><type fullname="<%=type:ToString()%>" preserve="all"/>
<%end)%>
</assembly>
<%end)%>
</linker>
~~~
TemplateCommon has some predefined functions that can be used (for example ForEachCsList). You can search in TemplateCommon.lua.txt of the project to see which functions are available. For ordinary Lua, you can write one.
## API
~~~csharp
public static void CSObjectWrapEditor.Generator.CustomGen(string template_src, GetTasks get_tasks)
~~~
* template_src: template source code
* get_tasks: This is a callback function. The type is GetTasks. This function is used to accept the user configuration and return the data that needs to be injected into the template and the output stream of the files.
~~~csharp
public delegate IEnumerable<CustomGenTask> GetTasks(LuaEnv lua_env, UserConfig user_cfg);
~~~
* lua_env: This is a LuaEnv object. Because the returned template data needs to be placed in LuaTable, LuaEnv.NewTable is required.
* user_cfg: user configuration
* return: Among the returned values, CustomGenTask represents a generated file, and IEnumerable type indicates that the same template can generate multiple files.
~~~csharp
public struct UserConfig
{
public IEnumerable<Type> LuaCallCSharp;
public IEnumerable<Type> CSharpCallLua;
public IEnumerable<Type> ReflectionUse;
}
~~~
~~~csharp
public struct CustomGenTask
{
public LuaTable Data;
public TextWriter Output;
}
~~~
Example:
~~~csharp
public static IEnumerable<CustomGenTask> GetTasks(LuaEnv lua_env, UserConfig user_cfg)
{
LuaTable data = lua_env.NewTable();
var assembly_infos = (from type in user_cfg.ReflectionUse
group type by type.Assembly.GetName().Name into assembly_info
select new { FullName = assembly_info.Key, Types = assembly_info.ToList()}).ToList();
data.Set("assembly_infos", assembly_infos);
yield return new CustomGenTask
{
Data = data,
Output = new StreamWriter(GeneratorConfig.common_path + "/link.xml",
false, Encoding.UTF8)
};
}
~~~
* Only one file is generated here, so only one CustomGenTask is returned.
* data is the data to be used in the template. There is an assembly_infos field included. See the template section for how to use this field.
## Tag
Generally speaking, you can use MenuItem to create a menu to trigger a custom generate operation. However, sometimes you may want the generate operation to be triggered directly by the xLua "Generate Code" menu. In this situation, you will need to use CSObjectWrapEditor.GenCodeMenu
Example:
~~~csharp
[GenCodeMenu]//加到Generate Code菜单里头
public static void GenLinkXml()
{
Generator.CustomGen(ScriptableObject.CreateInstance<LinkXmlGen>().Template.text, GetTasks);
}
~~~
PS: All the code related to the content above is in the XLua\Src\Editor\LinkXmlGen directory, which is also the implementation of the link.xml generation function that was explained at the beginning.
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c0765c3c416e8f746bf142b05be65ea7
timeCreated: 1529661500
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
+371
View File
@@ -0,0 +1,371 @@
# FAQs
## How to use xLua distribution package?
xLua is currently released as a zip package and can be extracted to the project directory.
## Can xLua be placed in another directory?
Yes, but the generated code directory needs to be configured (by default, it is in the Assets\XLua\Gen directory). For details, see the GenPath configuration in XLua Configuration.doc.
The important thing to note about changing directories is that the generated code and xLua core code must be in the same assembly. If you want to use the hotfix function, the xLua core code must be in the Assembly-CSharp assembly.
## Does Lua source code only use the txt extension?
It can use any extension.
If you want to add TextAsset to an installation package (for example, to the Resources directory), Unity does not identify the Lua extension. This is Unity's rule.
If you do not add it to the installation package, there is no limit to the extension. For example, in case that you download it to a directory (this is also practicable in hotfix mode), and then read this directory with CustomLoader or by setting package.path.
Why does the Lua source code (including examples) of xLua use the txt extension? Because xLua itself is a library, it doesn't provide download functionality, and it's inconvenience to download code from somewhere else during runtime. TextAsset is a simpler solution.
## The editor (or non-il2cpp for Android) runs normally, but when iOS calls a function, "attempt to call a nil value" is reported.
By default, il2cpp will strip code, such as engine code, C# system APIs, and third-party dlls. In simple terms, functions in these places will not be compiled into your final release package if your C# code does not access them.
Solution: Add a reference (for example, configuring it to LuaCallCSharp, or adding access to that function to your C# code), or use the link.xml configuration (When ReflectionUse is configured, xLua will automatically configure it for you in link.xml) to tell il2cpp not to strip a certain type of code.
## Where can I find Plugins source code and how can I use it?
Plugins source code is in the xLua_Project_Root/build.
The source code compilation relies on CMake. After installing CMake, execute make_xxxx_yyyy.zz. xxxx stands for the platform, such as iOS or Android; yyyy is the virtual machine to be integrated, including Lua 5.3 and LuaJIT. The file extension is zz. The extension is bat for Windows and sh for other platforms.
Windows compilation relies on Visual Studio 2015.
Android compilation uses Linux, relies on NDK, and needs to point ANDROID_NDK in the script to the installation directory of NDK.
iOS and OS X need to be compiled on a Mac.
## How do I solve the "xlua.access, no field __Hitfix0_Update" error?
Follow the [Hotfix Operation Guide](hotfix.md).
## How do I solve the "please install the Tools" error?
Do not install Tools to the same level directory as Assets. You can find Tools in the installation package, or in the master directory.
## How do I solve the "This delegate/interface must add to CSharpCallLua: XXX" error?
In the editor, xLua can run even without generating code. This prompt appears either because CSharpCallLua was not to the type, or because the code was generated before adding, but no generation was executed again.
Solution: After confirming that CSharpCallLua has been added to XXX (type name), clear the code and run it again.
If there is no problem with the editor, the error will be reported to the mobile phone. This means that you did not generate the code (execute “XLua/Generate Code”) before release.
## What do I do if executing "XLua/Hotfix Inject In Editor" menu on Unity 5.5 or later versions produces the following prompt: "WARNING: The runtime version supported by this application is unavailable."
This is because the injection tool was compiled with .NET 3.5. The Unity 5.5 warning means that MonoBleedingEdge's mono environment does not support .NET 3.5. However, due to backward compatibility, no real problems related to this warning have been found so far.
You may find that defining INJECT_WITHOUT_TOOL in nested mode will not produce this warning. However, the problem is that this mode is used for debugging and is not recommended because it may cause some library conflicts.
## How do I trigger an event in hotfix?
Firstly, enable private member access using xlua.private_accessible.
Then, call delegates using the "&event name" field of the object, for example self\['&MyEvent'\](), where MyEvent is the event name.
## How do I patch Unity Coroutine's implementation function?
See the corresponding section of the [Hotfix Operation Guide](hotfix.md).
## Is NGUI (or UGUI/DOTween, etc...) supported?
Yes. The most important feature of xLua is that what you write with C# can be originally replaced with Lua, and the plugins available on C# will remain available.
## If debugging is needed, how do I deal with the filepath parameter of CustomLoader?
When Lua calls require 'a.b', CustomLoader will be called and the string "a.b" will be injected. You need to understand this string, load the Lua file (from file/memory/network, etc...) and return two things. The first thing is a path that the debugger can understand, for example a/b.lua, which is returned by setting the filepath parameter of the ref type. The second thing is the bytes[] of the source code in UTF8 format, which is returned with the returned value.
## What is generated code?
XLua supports one kind of Lua-C# interaction technique, which implements interaction by generating adaptation code between the two. It has better performance and is therefore recommended.
Another interaction technique is reflection, which has less impact on the installation package and can be used in scenarios which have lower performance requirements and has installation package size limit.
## How do I solve errors with the code generated before and after changing the interface?
Clear the generated code (execute the "Clear Generated Code" menu, which may disappear after restart. Then you can manually delete the entire generated code directory), and then regenerate the code when the compilation is completed.
## When should the code be generated?
During the development period, it is not recommended that code be generated, to avoid many compilation failures due to inconsistency and waiting time during compilation of the generated code.
The generated code must be executed before the build version for the mobile phone. Automatic execution is recommended.
Optimize performance. The generated code must be executed before the performance test because there are significant differences between the generated code and the code not generated.
## Do all C# APIs in CS namespaces occupy high memory?
Due to the use of LazyLoad, their existences are just a virtual concepts. For example, for UnityEngine.GameObject, its methods, and properties are loaded only when accessing the first CS.UnityEngine.GameObject or transferring the first instance to Lua.
## In what scenarios are LuaCallSharp and CSharpCallLua used?
It depends on the caller and the callee. For example, if you want to call C#'s GameObject, find a function in Lua, or call GameObject's instance methods or properties, the GameObject type needs to be added to LuaCallSharp. If you want to add a Lua function to the UI callback (in this case, C# is the caller and the Lua function is the callee), the delegate declared by the callback needs to be added to CSharpCallLua.
Sometimes, it is confusing, like when calling List<int>, for example. Find(Predicate<int> match) and List<int> will of course be added to LuaCallSharp. However, Predicate<int> needs to be added to CSharpCallLua, because the caller of match is C#, and a Lua function is called.
A more unthinkable way: When you see, "This delegate/interface must add to CSharpCallLua: XXX", just add XXX to CSharpCallLua.
## Will gc alloc appear in value type transfer?
If you are using the delegate to call a Lua function, if the LuaTable and LuaFunction that you use have no gc interface, or if there is an array, the following value types have no gc:
1. All the basic value types (all integers, all floating-point numbers, decimals)
2. All enumerated types
3. The field contains only the struct of value type, and it can nest other struct.
For 2 and 3, pleases add those types to GCOptimize.
## Is reflection available on iOS?
There are two restrictions on iOS: 1. no JIT; 2. code stripping;
When C# calls Lua via delegates or interfaces, using reflection emit instead of the generated code relies on JIT, so this is currently only available in the editor mode.
If Lua calls C#, it will be mainly affected by code stripping. In this case, you can configure ReflectionUse (but not LuaCallSharp), and execute "Generate Code". No package code except link.xml will be generated for the type this time. Set this type to 'not to be stripped'.
In short, only CSharpCallLua is necessary (a little code of this type is generated), and reflection can be used in LuaCallSharp generation.
## Is calling generic methods supported?
This is partially supported. See [Example 9 for the degree of support.](../Examples/09_GenericMethod/)
There are other ways to call generic methods. If it is a static method, you can write a package to instantiate the generic method.
If it is a member method, xLua supports the extension method. You can add an extension method to instantiate a generic method. This extension method is just like an ordinary member method.
```csharp
// C#
public static Button GetButton(this GameObject go)
{
return go.GetComponent<Button>();
}
```
```lua
-- lua
local go = CS.UnityEngine.GameObject.Find("button")
go:GetButton().onClick:AddListener(function()
print('onClick')
end)
```
## Can Lua call C# overloaded functions?
Yes, but this is not as well supported as on C#. For example, in the overloaded methods void Foo(int a) and void Foo(short a), because int and short both correspond to the number on Lua, it is impossible to use the parameters to judge which overloaded method is called. In this situation, you can use the extension method to give an alias to one of them.
## What should I do if "A method/property/field is not defined" is reported when the code is generated during packaging, but it runs normally on the editor?
This is often because the method/property/field is extended in the conditional compilation, which is only available in UNITY_EDITOR. This can be solved by adding this method/property/field to the blacklist, and then re-executing code generation when the compilation is completed.
## Why is this[string field] or this[object field] operator overload inaccessible in Lua? (For example, in Dictionary\<string, xxx\>, I cannot retrieve values in Dictionary\<object, xxx\> using dic['abc'] or dic.abc on Lua)
Because: 1. This feature will cause the base type-defined methods, properties, and fields to be inaccessible. (For example, the Animation cannot access the GetComponent method.); 2. The data cannot be retrieved if the key is the name of a method, property, or field of the current type. For example, in the Dictionary type, dic['TryGetValue'] returns a function that points to the TryGetValue method of the Dictionary.
If your version is newer than 2.1.11, you can use get_Item to get the value and set_Item to set the value. It should be noted that only this[string field] or this[object field] operator has these two alternative APIs. The keys of other types do not.
~~~lua
dic:set_Item('a', 1)
dic:set_Item('b', 2)
print(dic:get_Item('a'))
print(dic:get_Item('b'))
~~~
If your version is 2.1.11 or earlier, it is recommended that you directly use the equivalent method of the operator, such as the TryGetValue of the Dictionary. If this method is not available, you can package a method through the Extension method on C# and then use it.
## Why are some Unity objects null in C# but not nil in Lua, for example, a GameObject that has been destroyed?
In fact, the C# object is not null, but the UnityEngine.Object overloads the == operator. When an object is destroyed, or in the case of failed initialization, true is returned for obj == null, but this C# object is not null. You can check this using System.Object.ReferenceEquals(null, obj).
In this case, you can write an extension method for UnityEngine.Object.
~~~csharp
[LuaCallCSharp]
[ReflectionUse]
public static class UnityEngineObjectExtention
{
public static bool IsNull(this UnityEngine.Object o) // 或者名字叫IsDestroyed等等
{
return o == null;
}
}
~~~
Then, in Lua, use IsNull for all UnityEngine.Object instances.
~~~lua
print(go:GetComponent('Animator'):IsNull())
~~~
## How do I construct a generic instance?
If the types involved are all in the mscorlib and Assembly-CSharp assembly, the construction of the generic instance is the same as that of ordinary types. Both are CS.namespace.typename(). The typename expression is special, and the typename expression of the generic instance contains the identifier illegal symbol. The last part should be replaced with ["typename"]. Use List<string> as an example.
~~~lua
local lst = CS.System.Collections.Generic["List`1[System.String]"]()
~~~
If the typename of a generic instance is undefined, typeof(undefined type).ToString() can be printed on C#.
If the types involved are not in the mscorlib and Assembly-CSharp assembly, you can use the C# reflection:
~~~lua
local dic = CS.System.Activator.CreateInstance(CS.System.Type.GetType('System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[UnityEngine.Vector3, UnityEngine]],mscorlib'))
dic:Add('a', CS.UnityEngine.Vector3(1, 2, 3))
print(dic:TryGetValue('a'))
~~~
If your xLua version is larger than v2.1.12, you can
~~~lua
-- local List_String = CS.System.Collections.Generic['List<>'](CS.System.String) -- another way
local List_String = CS.System.Collections.Generic.List(CS.System.String)
local lst = List_String()
local Dictionary_String_Vector3 = CS.System.Collections.Generic.Dictionary(CS.System.String, CS.UnityEngine.Vector3)
local dic = Dictionary_String_Vector3()
dic:Add('a', CS.UnityEngine.Vector3(1, 2, 3))
print(dic:TryGetValue('a'))
~~~
## Why is the "try to dispose a LuaEnv with C# callback!" error reported when LuaEnv.Dispose is called?
This is because C# still has a delegate pointing to a function in the Lua virtual machine. Check this to prevent calling these invalid delegates after the virtual machine is released (They are invalid because the virtual machine on which the Lua function is referenced has been released.) from causing exceptions or even crashes.
How do I solve this? Release these delegates, that is the delegates nonexistent on C# that will not be referenced:
If you get and save the object member through LuaTable.Get on C#, then assign null to the member.
If you register the Lua function in Lua to some event callbacks, then deregister these callbacks.
If you perform an injection to C# via xlua.hotfix(class, method, func), then delete it via xlua.hotfix(type, method, nil).
Note that this should be done before Dispose.
## Crashes occur when calling LuaEnv.Dispose.
It is very likely that this Dispose operation is executed by Lua, which is equivalent to releasing the Lua virtual machine during the execution by Lua. Thus, this can only be executed by C#.
## When the C# parameter (or field) type is object, the integer is transferred as the long type by default. How do I specify other types like int, for example?
See [Example 11](../Examples/11_RawObject/RawObjectTest.cs)
## How do I execute the original C# logic before executing hotfix?
With util.hotfix_ex, you can call the original C# logic.
~~~lua
local util = require 'xlua.util'
util.hotfix_ex(CS.HotfixTest, 'Add', function(self, a, b)
local org_sum = self:Add(a, b)
print('org_sum', org_sum)
return a + b
end)
~~~
## How do I assign a C# function to a delegate field?
In 2.1.8 or earlier versions, you can just use the C# function as a Lua function. The performance is lower, because the delegate calls Lua through the Birdage adaptation code first, and then Lua calls C#.
In 2.1.9, the createdelegate function is added in xlua.util.
For example, see the following C# code:
~~~csharp
public class TestClass
{
public void Foo(int a)
{
}
public static void SFoo(int a)
{
}
public delegate void TestDelegate(int a);
~~~
You can specify using the Foo function to create a TestDelegate instance.
~~~lua
local util = require 'xlua.util'
local d1 = util.createdelegate(CS.TestDelegate, obj, CS.TestClass, 'Foo', {typeof(CS.System.Int32)}) --由于Foo是实例方法,所以参数2需要传TestClass实例
local d2 = util.createdelegate(CS.TestDelegate, nil, CS.TestClass, 'SFoo', {typeof(CS.System.Int32)})
obj_has_TestDelegate.field = d1 + d2 --到时调用field的时候将会触发Foo和SFoo,这不会经过Lua适配
~~~
## Why is Lua sometimes interrupted without an error message?
Generally, this happens in two situations:
1. You used a coroutine instead of the standard Lua to run the code that had an error. Coroutine errors are expressed via the returned resume value. Please refer to the relevant Lua official documents for more information. If you want to throw an exception directly for the coroutine error, you can add an assert in your resume call.
Change the following code:
~~~lua
coroutine.resume(co, ...)
~~~
to:
~~~lua
assert(coroutine.resume(co, ...))
~~~
2. Print doesn't work after upper layer catch.
For example, in some SDKs, the exception disappears after try-catch during callback.
## How do I deal with overload ambiguity?
For example, due to ignoring the out parameter, one of the overloads of Physics.Raycast cannot be called. (For example, short and int cannot be distinguished.)
First, its rare that the out parameter causes overload ambiguity. At present, we have only received such feedback about Physics.Raycast (as of Sept. 22, 2017). We recommended that you solve this via self-packaging (This is also applicable to short and int): Directly package static functions with other names. For a member method, package it with the Extension method.
In the hotfix scenario, we do not package it in advance. How do I call the specified overload?
Use xlua.tofunction and reflection. xlua.tofunction inputs a MethodBase object and returns a Lua function. For example, see the following C# code:
~~~csharp
class TestOverload
{
public int Add(int a, int b)
{
Debug.Log("int version");
return a + b;
}
public short Add(short a, short b)
{
Debug.Log("short version");
return (short)(a + b);
}
}
~~~
We can call the specified overload this way:
~~~lua
local m1 = typeof(CS.TestOverload):GetMethod('Add', {typeof(CS.System.Int16), typeof(CS.System.Int16)})
local m2 = typeof(CS.TestOverload):GetMethod('Add', {typeof(CS.System.Int32), typeof(CS.System.Int32)})
local f1 = xlua.tofunction(m1) --切记对于同一个MethodBase,只tofunction一次,然后重复使用
local f2 = xlua.tofunction(m2)
local obj = CS.TestOverload()
f1(obj, 1, 2) --调用short版本,成员方法,所以要传对象,静态方法则不需要
f2(obj, 1, 2) --调用int版本
~~~
Note: Since xlua.tofunction is not easy to use and reflection can also be used, doing this is recommended only as a temporary solution. Instead, try doing this with a package method.
## Is interface expansion method supported?
Due to the quantity of generated code, calling with obj:ExtentionMethod() is not supported. You can only call CS.ExtentionClass.ExtentionMethod(obj) through the static method.
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7b6232183a3c52f4f9af4b68f8a687cc
timeCreated: 1529661499
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,354 @@
## Usage
1. Add the HOTFIX_ENABLE macro to enable this feature (to File->Build Setting->Scripting Define Symbols on Unity3D). This macro should be set separately on the editor and each mobile platform! For automatic packaging, it should be noted that the macro set in the code in the API are not valid and it needs to be set in the editor.
(It is recommended that you use HOTFIX_ENABLE only for mobile phone build versions or for developing a patch in the editor, but not for usual development codes)
2. Execute the XLua/Generate Code menu.
3. This step will be automatically carried out in injection and construction of mobile phone packages. You need to manually execute the "XLua/Hotfix Inject In Editor" menu when developing a hotfix in the editor. After successful injection, "hotfix inject finish!" or "had injected!" will be printed.
## Constraints
Static constructors are not supported.
At present, only the code hotfix for Assets is supported, but not the code hotfix for engine or for the C# system library.
## API
xlua.hotfix(class, [method_name], fix)
* Description: Inject Lua patch
* class: C# class, two representation methods, CS.Namespace.Typename or string method "Namespace.Typename"; The string format is consistent with the Type.GetType of C#; If the nested type is non-Public type, you can only use the string method to represent "Namespace.TypeName+NestedTypeName".
* method_name: method name, optional;
* fix: If you transfer method_name, fix will be a function; otherwise it will provide a set of functions through the table. The table organization is based on the principle that key is method_name and value is the function.
base(csobj)
* Description: The child type override function is implemented by calling the parent type via base.
* csobj: object
* Returned value: new object, which can be used to call the method via base
Example (in HotfixTest2.cs):
```lua
xlua.hotfix(CS.BaseTest, 'Foo', function(self, p)
print('BaseTest', p)
base(self):Foo(p)
end)
```
util.hotfix_ex(class, method_name, fix)
* Description: This is the enhanced version of xlua.hotfix, which can perform the original function in the fix function. Its disadvantage is that the implementation of fix will be slightly slower.
* method_name: method name;
* fix: This is the Lua function used to replace the C# method.
## Tag the type of hotfix
Like other configurations, there are two methods:
Configure a list in the static field or property of a static type. Properties can be used to implement more complex configurations, such as whitelisting based on Namespace.
~~~csharp
public static class HotfixCfg
{
[Hotfix]
public static List<Type> by_field = new List<Type>()
{
typeof(HotFixSubClass),
typeof(GenericClass<>),
};
[Hotfix]
public static List<Type> by_property
{
get
{
return (from type in Assembly.Load("Assembly-CSharp").GetTypes()
where type.Namespace == "XXXX"
select type).ToList();
}
}
}
~~~
## Hotfix Flag
The Hotfix flag can set some flags to customize the generated code and instrumentation.
* Stateless and Stateful
This is is a legacy setting. The Stateful method has been removed in the new version because similar effects can be achieved with the xlua.util.state interface. For how to use this interface, see the sample code in HotfixTest2.cs.
Without Stateful, the default is Stateless, so there is no need to set this flag.
* ValueTypeBoxing
Value type adaptation. The delegate will converge to the object. Its advantage lies in having less code. Its disadvantage is that boxing and gc are generated. It is suitable for text segment sensitive services.
* IgnoreProperty
No property injection and generation of adaptation code. In general, most properties have simple implementation and lower error probability. No injection is recommended.
* IgnoreNotPublic
No injection or generation of adaptation code for non-public methods. Only private methods (such as MonoBehaviour) which are called by reflection will be injected. Other non-public methods that are only called by this type may not be injected. but the workload for repair will be slightly heavier. All public methods that reference this function must be rewritten.
* Inline
No generation of the adaptation delegate. The process code is directly injected into the function body.
* IntKey
Instead of generating static fields, all injection points are managed centrally in an array.
Advantages: Little effect on the text segment.
Disadvantages: This is not as convenient as the default method, and needs to use the id to indicate which function the hotfix is used for. This id is assigned when the code is injected into the tool. The function-id mapping will be saved in Gen/Resources/hotfix_id_map.lua.txt. Automatic timestamping is backed up to the same level directory as hotfix_id_map.lua.txt. After releasing the mobile version, properly save the file.
The format of this file is like this (Note: This file is only used in IntKey mode. If you do not specify IntKey mode injection, only an empty table is returned in the file):
~~~lua
return {
["HotfixTest"] = {
[".ctor"] = {
5
},
["Start"] = {
6
},
["Update"] = {
7
},
["FixedUpdate"] = {
8
},
["Add"] = {
9,10
},
["OnGUI"] = {
11
},
},
}
~~~
To replace the Update function of HotfixTest, you have to
~~~lua
CS.XLua.HotfixDelegateBridge.Set(7, func)
~~~
If it is an overloaded function, a function name will correspond to multiple ids, such as the Add function above.
Can it be automated? Yes. xlua.util provides the auto_id_map function. When it is executed once, you can use the type and method name to indicate the repaired function.
~~~lua
(require 'xlua.util').auto_id_map()
xlua.hotfix(CS.HotfixTest, 'Update', function(self)
self.tick = self.tick + 1
if (self.tick % 50) == 0 then
print('<<<<<<<<Update in lua, tick = ' .. self.tick)
end
end)
~~~
The premise is that hotfix_id_map.lua.txt is in a directory that can be referenced by require 'hotfix_id_map'.
## Usage suggestions
* Add the Hotfix flag to all types that are most likely to be modified.
* It is recommended that you use reflection to find all delegate types involved in the function parameters, fields, properties and events, and then add CSharpCallLua.
* Add LuaCallCSharp to business code, engine APIs, system APIs, and the types to which the Lua hotfix requires high-performance access.
* Engine APIs and system APIs may be stripped by code (those not referenced by C# will be stripped). If you think you may add API calls other than C# codes, these APIs are added to either LuaCallCSharp or ReflectionUse.
## Patching
Xlua uses Lua functions to replace C#'s constructors, functions, properties and events. Lua implementations are based on functions. For example, the property corresponds to a getter function and a setter function, and the event corresponds to an add function and a remove function.
* Functions
method_name transfers the function name and supports overload. Different overloads are forwarded to the same Lua function.
For example:
```csharp
// 要fix的C#类
[Hotfix]
public class HotfixCalc
{
public int Add(int a, int b)
{
return a - b;
}
public Vector3 Add(Vector3 a, Vector3 b)
{
return a - b;
}
```
```lua
xlua.hotfix(CS.HotfixCalc, 'Add', function(self, a, b)
return a + b
end)
```
The difference between a static function and a member function is that a member function will have a self parameter added. This self is the C# object itself in Stateless mode (corresponding to C#'s this).
Ordinary parameters correspond to Lua parameters. The ref parameter corresponds to a Lua parameter and a returned value, and the out parameter corresponds to a returned value on Lua.
Generic functions have the same hotfix rules as ordinary functions.
* Constructors
The method_name corresponding to the constructor is ".ctor".
Unlike the ordinary function, the hotfix of the constructor is not a replacement, but calls Lua after executing the original logic.
* Properties
The property named "AProp" will correspond to a getter, its method_name equals to get_AProp, and the setter's method_name is equal to set_AProp.
* []Operators
The setter corresponds to set_Item, and the getter corresponds to get_Item. The first parameter is self, which is followed by key and value for the setter. The getter has only the key parameter, and the returned value is the value that it has gotten.
* Other operators
The operators of C# have a set of internal representations. For example, the + operator function name is op_Addition (for the internal representation of other operators, see the relevant document). Overriding this function will override the + operator of C#.
* Events
For example, for the event "AEvent", the += operator is add_AEvent, and the -= operator is remove_AEvent. The first parameter of the two functions is self, and the second parameter is the delegate immediately after the operator.
Then, directly access the private delegate corresponding to the event via xlua.private_accessible. (For versions newer than 2.1.11, calling xlua.private_accessible is not required.) The event can be triggered directly through the object's "&event name" field, such as self\['&MyEvent'\](), where MyEvent is the event name.
* Destructors
The method_name is "Finalize” and transfers a self parameter.
Unlike the ordinary function, the hotfix of the destructor is not a replacement, but continues the original logic after calling the Lua function.
* Generic types
Other rules are the same. It should be noted that each instantiated generic type is an independent type. You can only patch each instantiated type. For example:
```csharp
public class GenericClass<T>
{
```
You can only patch GenericClass\<double\> and GenericClass\<int\>, instead of GenericClass.
The following is an example of patching GenericClass<double>:
```csharp
luaenv.DoString(@"
xlua.hotfix(CS.GenericClass(CS.System.Double), {
['.ctor'] = function(obj, a)
print('GenericClass<double>', obj, a)
end;
Func1 = function(obj)
print('GenericClass<double>.Func1', obj)
end;
Func2 = function(obj)
print('GenericClass<double>.Func2', obj)
return 1314
end
})
");
```
* Unity coroutine
Using util.cs_generator, you can simulate an IEnumerator with a function. The coroutine.yield here is similar to the yield return in C#. For example, the following C# code is equivalent to the corresponding hotfix code.
~~~csharp
[XLua.Hotfix]
public class HotFixSubClass : MonoBehaviour {
IEnumerator Start()
{
while (true)
{
yield return new WaitForSeconds(3);
Debug.Log("Wait for 3 seconds");
}
}
}
~~~
~~~csharp
luaenv.DoString(@"
local util = require 'xlua.util'
xlua.hotfix(CS.HotFixSubClass,{
Start = function(self)
return util.cs_generator(function()
while true do
coroutine.yield(CS.UnityEngine.WaitForSeconds(3))
print('Wait for 3 seconds')
end
end)
end;
})
");
~~~
* Entire type
If you want to replace the entire type, you don't need to call xlua.hotfix again and again, you can do it all at once. Just give a table and press method_name = function.
```lua
xlua.hotfix(CS.StatefullTest, {
['.ctor'] = function(csobj)
return util.state(csobj, {evt = {}, start = 0, prop = 0})
end;
set_AProp = function(self, v)
print('set_AProp', v)
self.prop = v
end;
get_AProp = function(self)
return self.prop
end;
get_Item = function(self, k)
print('get_Item', k)
return 1024
end;
set_Item = function(self, k, v)
print('set_Item', k, v)
end;
add_AEvent = function(self, cb)
print('add_AEvent', cb)
table.insert(self.evt, cb)
end;
remove_AEvent = function(self, cb)
print('remove_AEvent', cb)
for i, v in ipairs(self.evt) do
if v == cb then
table.remove(self.evt, i)
break
end
end
end;
Start = function(self)
print('Start')
for _, cb in ipairs(self.evt) do
cb(self.start, 2)
end
self.start = self.start + 1
end;
StaticFunc = function(a, b, c)
print(a, b, c)
end;
GenericTest = function(self, a)
print(self, a)
end;
Finalize = function(self)
print('Finalize', self)
end
})
```
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7919d0bddff7ee340852008d36cd68ec
timeCreated: 1529661499
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: ee1eae11fbe87b04193ab2c3d15ba2b3
folderAsset: yes
timeCreated: 1481715983
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,31 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: logo
m_Shader: {fileID: 7, guid: 0000000000000000f000000000000000, type: 0}
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _MainTex:
m_Texture: {fileID: 2800000, guid: 6b9f4e2e38c36db40bc5bdfe20038d94, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats: []
m_Colors:
- _Color: {r: 1, g: 1, b: 1, a: 1}
m_BuildTextureStacks: []
@@ -0,0 +1,4 @@
fileFormatVersion: 2
guid: 953e2ba39b9a2d54388919b75877efb7
NativeFormatImporter:
userData:
Binary file not shown.
@@ -0,0 +1,4 @@
fileFormatVersion: 2
guid: f05e875da3e1b3844b2360d177d617c9
DefaultImporter:
userData:
@@ -0,0 +1,328 @@
## C# API
### LuaEnv类
#### object[] DoString(string chunk, string chunkName = "chuck", LuaTable env = null)
描述:
执行一个代码块。
参数:
chunk: Lua代码的字符串;
chunkName 发生error时的debug显示信息中使用,指明某某代码块的某行错误;
env :这个代码块的环境变量;
返回值:
代码块里return语句的返回值;
比如:return 1, “hello”,DoString返回将包含两个object的数组, 一个是double类型的1 一个是string类型的“hello”
例如:
LuaEnv luaenv = new LuaEnv();
object[] ret = luaenv.DoString("print(hello)\r\nreturn 1")
UnityEngine.Debug.Log("ret="+ret[0]);
luaenv.Dispose()
#### T LoadString<T>(string chunk, string chunkName = "chunk", LuaTable env = null)
描述:
加载一个代码块,但不执行,只返回类型可以指定为一个delegate或者一个LuaFunction
参数:
chunk: Lua代码的字符串;
chunkName 发生error时的debug显示信息中使用,指明某某代码块的某行错误;
env :这个代码块的环境变量;
返回值:
代表该代码块的delegate或者LuaFunction类;
#### LuaTable Global;
描述:
代表lua全局环境的LuaTable
### void Tick()
描述:
清除Lua的未手动释放的LuaBase对象(比如:LuaTable LuaFunction),以及其它一些事情。
需要定期调用,比如在MonoBehaviour的Update中调用。
### void AddLoader(CustomLoader loader)
描述:
增加一个自定义loader
参数:
loader:一个包括了加载函数的委托,其类型为delegate byte[] CustomLoader(ref string filepath),当一个文件被require时,这个loader会被回调,其参数是调用require所使用的参数,如果该loader找到文件,可以将其读进内存,返回一个byte数组。如果需要支持调试的话,而filepath要设置成IDE能找到的路径(相对或者绝对都可以)
#### void Dispose()
描述:
Dispose该LuaEnv。
> LuaEnv的使用建议:全局就一个实例,并在Update中调用GC方法,完全不需要时调用Dispose
### LuaTable类
#### T Get<T>(string key)
描述:
获取在key下,类型为T的value,如果不存在或者类型不匹配,返回null;
#### T GetInPath<T>(string path)
描述:
和Get的区别是,这个函数会识别path里头的“.”,比如var i = tbl.GetInPath<int>(“a.b.c”)相当于在lua里头执行i = tbl.a.b.c,避免仅为了获取中间变量而多次调用Get,执行效率更高。
#### void SetInPath<T>(string path, T val)
描述:
和GetInPaht<T>对应的setter
#### void Get<TKey, TValue>(TKey key, out TValue value)
描述:
上面的API的Key都只能是string,而这个API无此限制;
#### void Set<TKey, TValue>(TKey key, TValue value)
描述:
对应Get<TKey, TValue>的setter
#### T Cast<T>()
描述:
把该table转成一个T指明的类型,可以是一个加了CSharpCallLua声明的interface,一个有默认构造函数的class或者struct,一个DictionaryList等等。
#### void SetMetaTable(LuaTable metaTable)
描述:
设置metaTable为table的metatable
### LuaFunction类
> 注意:用该类访问Lua函数会有boxing,unboxing的开销,为了性能考虑,需要频繁调用的地方不要用该类。建议通过table.Get<ABCDelegate>获取一个delegate再调用(假设ABCDelegate是C#的一个delegate)。在使用使用table.Get<ABCDelegate>之前,请先把ABCDelegate加到代码生成列表。
#### object[] Call(params object[] args)
描述:
以可变参数调用Lua函数,并返回该调用的返回值。
#### object[] Call(object[] args, Type[] returnTypes)
描述:
调用Lua函数,并指明返回参数的类型,系统会自动按指定类型进行转换。
#### void SetEnv(LuaTable env)
描述:
相当于lua的setfenv函数。
## Lua API
### CS对象
#### CS.namespace.class(...)
描述:
调用一个C#类型的构造函数,并返回类型实例
例如:
local v1=CS.UnityEngine.Vector3(1,1,1)
#### CS.namespace.class.field
描述:
访问一个C#静态成员
例如:
Print(CS.UnityEngine.Vector3.one)
#### CS.namespace.enum.field
描述:
访问一个枚举值
#### typeof函数
描述:
类似C#里头的typeof关键字,返回一个Type对象,比如GameObject.AddComponent其中一个重载需要一个Type参数
例如:
newGameObj:AddComponent(typeof(CS.UnityEngine.ParticleSystem))
#### 无符号64位支持
##### uint64.tostring
描述:
无符号数转字符串。
##### uint64.divide
描述:
无符号数除法。
##### uint64.compare
描述:
无符号比较,相对返回0,大于返回正数,小于返回负数。
##### uint64.remainder
描述:
无符号数取模。
##### uint64.parse
描述:
字符串转无符号数。
#### xlua.structclone
描述:
克隆一个c#结构体
#### xlua.private_accessible(class)
描述:
让一个类的私有字段,属性,方法等可用
例子:
xlua.private_accessible(CS.UnityEngine.GameObject)
#### xlua.get_generic_method
描述:
获取一个泛型方法
例子:
~~~lua
local foo_generic = xlua.get_generic_method(CS.GetGenericMethodTest, 'Foo')
local bar_generic = xlua.get_generic_method(CS.GetGenericMethodTest, 'Bar')
local foo = foo_generic(CS.System.Int32, CS.System.Double)
local bar = bar_generic(CS.System.Double, CS.UnityEngine.GameObject)
-- call instance method
local o = CS.GetGenericMethodTest()
local ret = foo(o, 1, 2)
print(ret)
-- call static method
bar(2, nil)
~~~
#### cast函数
描述:
指明以特定的接口访问对象,这在实现类无法访问的时候(比如internal修饰)很有用,这时可以这么来(假设下面的calc对象实现了C#的PerformentTest.ICalc接口)
例子:
cast(calc, typeof(CS.PerformentTest.ICalc))
然后就木有其它API了
访问csharp对象和访问一个table一样,调用函数跟调用lua函数一样,也可以通过操作符访问c#的操作符,下面是一个例如:
local v1=CS.UnityEngine.Vector3(1,1,1)
local v2=CS.UnityEngine.Vector3(1,1,1)
v1.x = 100
v2.y = 100
print(v1, v2)
local v3 = v1 + v2
print(v1.x, v2.x)
print(CS.UnityEngine.Vector3.one)
print(CS.UnityEngine.Vector3.Distance(v1, v2))
## 类型映射
### 基本数据类型
|C#类型|Lua类型|
|-|-|
|sbytebyteshortushortintuintdoublecharfloat|number|
|decimal|userdata|
|longulong|userdata/lua_Integer(lua53)|
|bytes[]|string|
|bool|boolean|
|string|string|
### 复杂数据类型
|C#类型|Lua类型|
|-|-|
|LuaTable|table|
|LuaFunction|function|
|class或者 struct的实例|userdatatable|
|methoddelegate|function|
#### LuaTable
C#侧指明从Lua侧输入(包括C#方法的输入参数或者Lua方法的返回值LuaTable类型,则要求Lua侧为table。或者Lua侧的table,在C#侧未指明类型的情况下转换成LuaTable
#### LuaFunction
C#侧指明从Lua侧输入(包括C#方法的输入参数或者Lua方法的返回值LuaFunction类型,则要求Lua侧为function。或者Lua侧的function,在C#侧未指明类型的情况下转换成LuaFunction
#### LuaUserData
对应非C# Managered对象的lua userdata。
#### class或者 struct的实例:
从C#传一个class或者struct的实例,将映射到Lua的userdata,并通过__index访问该userdata的成员
C#侧指明从Lua侧输入指定类型对象Lua侧为该类型实例的userdata可以直接使用;如果该指明类型有默认构造函数,Lua侧是table则会自动转换,转换规则是:调用构造函数构造实例,并用table对应字段转换到c#对应值后赋值各成员
#### method delegate
成员方法以及delegate都是对应lua侧的函数。
C#侧的普通参数以及引用参数,对应lua侧函数参数;C#侧的返回值对应于Lua的第一个返回值;引用参数和out参数则按序对应于Lua的第2到第N个参数。
## 宏
#### HOTFIX_ENABLE
打开hotfix功能。
#### NOT_GEN_WARNING
反射时打印warning。
#### GEN_CODE_MINIMIZE
以偏向减少代码段的方式生成代码。
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d97e183eaa307684b9b99ded9005b181
timeCreated: 1518577405
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,305 @@
## C# APIs
### LuaEnv type
#### object[] DoString(string chunk, string chunkName = "chuck", LuaTable env = null)
Description:
Executes a code block.
Parameter:
chunk: Lua code string;
chunkName: This is used in the debug message when an error occurs, indicating a certain line in a certain code block has an error.
env: This is the environment variable of this code block.
Returned value:
The returned value of the return statement in the code block.
For example: return 1, "hello". DoString returns an array that will contain two objects. One is 1 of the double type, and the other is the string "hello".
For example:
LuaEnv luaenv = new LuaEnv();
object[] ret = luaenv.DoString("print(hello)\r\nreturn 1")
UnityEngine.Debug.Log("ret="+ret[0]);
luaenv.Dispose()
#### T LoadString<T>(string chunk, string chunkName = "chunk", LuaTable env = null)
Description:
This loads a code block, but does not execute it. It only returns the type can be specified as a delegate or a LuaFunction.
Parameter:
chunk: Lua code string;
chunkName: This is used in the debug message when an error occurs, indicating a certain line in a certain code block has an error.
env: This is the environment variable of this code block.
Returned Value:
This is the delegate or LuaFunction type that represents the code block.
#### LuaTable Global;
Description:
This is the LuaTable representing the Lua global environment.
### void Tick()
Description:
This clears Lua's LuaBase objects that have not been manually released (for example LuaTable, LuaFunction), and other things.
This needs to be called periodically, for example in the Update of MonoBehaviour.
### void AddLoader(CustomLoader loader)
Description:
Adds a custom loader
Parameter:
loader: A delegate that includes the loaded function. The type is delegate byte[] CustomLoader(ref string filepath). When a file is required, the loader will be called back. Its parameters are the parameters used to call require. If the loader finds the file, it reads it into memory and returns a byte array. If debug support is required, the filepath should be set to one the IDE can find (relative or absolute).
#### void Dispose()
Description:
This disposes the LuaEnv.
> LuaEnv usage suggestion: use only one instance globally. Call the GC method in Update, and call Dispose when it is not required.
### LuaTable type
#### T Get<T>(string key)
Description:
Gets the value of type T on key. Null is returned if it does not exist or the type does not match.
#### T GetInPath<T>(string path)
Description:
The difference from Get is that this function will identify the "." in the path. For example, var i = tbl.GetInPath<int>(“a.b.c”) is equivalent to executing i = tbl.abc in Lua. This avoids calling Get multiple times and obtaining intermediate variables. It has higher execution efficiency.
#### void SetInPath<T>(string path, T val)
Description:
Setter corresponding to SetInPath<T>;
#### void Get<TKey, TValue>(TKey key, out TValue value)
Description:
The key of the APIs described above can only be a string, but this API has no such restriction.
#### void Set<TKey, TValue>(TKey key, TValue value)
Description:
This is the setter corresponding to Get<TKey, TValue>.
#### T Cast<T>()
Description:
Converts the table to a type specified by T. It can be an interface with a CSharpCallLua declaration, a type or struct with a default constructor, a Dictionary, a List, and so on.
#### void SetMetaTable(LuaTable metaTable)
Description:
Sets metatable to a table metatable
### LuaFunction type
> Note: Accessing Lua functions with this type will result in overhead from boxing and unboxing. For the sake of performance, do not use this type if frequent calls are required. It is recommended that you use table.Get<ABCDelegate> to get a delegate and then call it (assuming ABCDelegate is a delegate of C#). Before using table.Get<ABCDelegate>, add ABCDelegate to the list of generated code.
#### object[] Call(params object[] args)
Description:
This calls the Lua function with the variable parameters and returns the returned value of the call.
#### object[] Call(object[] args, Type[] returnTypes)
Description:
This calls the Lua function and specifies the type of the returned parameter. The system will automatically convert the specified type.
#### void SetEnv(LuaTable env)
Description:
Equivalent to Lua's setfenv function.
## Lua API
### CS objects
#### CS.namespace.class(...)
Description:
This calls a C# type constructor and returns a type instance.
For Example:
local v1=CS.UnityEngine.Vector3(1,1,1)
#### CS.namespace.class.field
Description:
This accesses a C# static member.
For Example:
Print(CS.UnityEngine.Vector3.one)
#### CS.namespace.enum.field
Description:
This accesses an enumerated value.
#### Typeof Functions
Description:
This is similar to the typeof keyword in C#: a Type object is returned. For example, in GameObject.AddComponent, one overload requires a Type parameter.
For Example:
newGameObj:AddComponent(typeof(CS.UnityEngine.ParticleSystem))
#### Unsigned 64-bit is supported
##### uint64.tostring
Description:
Unsigned number to string.
##### uint64.divide
Description:
Unsigned number division.
##### uint64.compare
Description:
Unsigned comparison: 0 is returned for equal, positive for greater than, and negative for less than.
##### uint64.remainder
Description:
Unsigned modulus.
##### uint64.parse
Description:
String to unsigned number.
#### xlua.structclone
Description:
This clones a c# structure.
#### xlua.private_accessible(class)
Description:
This makes the private fields, properties, methods of a type available.
#### Cast Function
Description:
This indicates that the object is accessed with a specific interface, which is useful when the implementation type is inaccessible (such as internal modification). Use it in the following way (assuming that the following calc object implements C#'s PerformentTest.ICalc interface):
For Example:
cast(calc, typeof(CS.PerformentTest.ICalc))
Then, no other APIs are available.
Accessing a csharp object is like accessing a table. Calling a function is like calling the Lua function. Operators can also be used to access the C# operator. Here is an example:
local v1=CS.UnityEngine.Vector3(1,1,1)
local v2=CS.UnityEngine.Vector3(1,1,1)
v1.x = 100
v2.y = 100
print(v1, v2)
local v3 = v1 + v2
print(v1.x, v2.x)
print(CS.UnityEngine.Vector3.one)
print(CS.UnityEngine.Vector3.Distance(v1, v2))
## Type mapping
### Basic data type
|C# type|Lua type|
|-|-|
|sbyte, byte, short, ushort, int ,uint ,double ,char ,float|number|
|decimal|userdata|
|long ,ulong|userdata/lua_Integer(lua53)|
|bytes[]|string|
|bool|boolean|
|string|string|
### Complex data type
|C# type|Lua type|
|-|-|
|LuaTable|table|
|LuaFunction|function|
|class or struct instance|userdata, table|
|method, delegate|function|
#### LuaTable:
If C# specifies inputting the LuaTable type (including the input parameters of the C# method or the returned value of the Lua method) from Lua, then it must be a table in Lua. Or, if C# does not specify the type, the table in Lua should be converted to LuaTable .
#### LuaFunction:
If C# specifies inputting the LuaFunction type (including the input parameters of the C# method or the returned value of the Lua method) from Lua, then it must be a function in Lua. Or, if C# does not specify the type, the function on Lua should be converted to LuaFunction.
#### LuaUserData:
This is Lua userdata corresponding to the non-C# Managered object.
#### Class or struct instance:
This transfers a class or struct instance from C#, maps it to Lua userdata, and accesses the member of the userdata via __index.
If C# specifies inputting objects of the specified type from Lua, then the userdata of the type instance is used directly in Lua. If the specified type has a default constructor, the table in Lua is automatically converted. The conversion rule is: call the constructor to construct an instance, and convert the field corresponding to table to each setter member in C#.
#### Method and delegate:
Both member methods and delegates correspond to the Lua functions.
The ordinary parameters and reference parameters on C# correspond to the Lua function parameters. The returned value on C# corresponds to the first returned value on Lua. The reference parameters and out parameters correspond to the 2nd to Nth parameters on Lua in sequence.
## Macros
#### HOTFIX_ENABLE
This enables the hotfix function.
#### NOT_GEN_WARNING
This prints warning when there is reflection.
#### GEN_CODE_MINIMIZE
Generates code in a way that minimizes the code segments.
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 563bce1ceae59334c860773faa7b3a7a
timeCreated: 1529661499
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,295 @@
## xLua Tutorial
### Load Lua files
1. Execute strings
The most basic way to execute a string is to use LuaEnv.DoString. The string must be compliant with Lua syntax.
For example:
luaenv.DoString("print('hello world')")
See the full code in the XLua\Tutorial\LoadLuaScript\ByString directory.
> However, this mode is not recommended. We recommend the following mode:
2. Load Lua files.
Use Lua's require function.
DoString("require 'byfile'")
See the full code in the XLua\Tutorial\LoadLuaScript\ByFile directory.
The require actually calls the loaders one by one to load the file. The loading process stops if one loader succeeds. If all loaders fail, no file found will be reported.
In addition to native loaders, xLua also adds loaders loaded from the Resource. Note that because the Resource supports only limited numbers of extensions, the Lua file under Resources must be added the txt extension (see the attached example).
The recommended way to load Lua scripts is as follows: Ensure the entire program is a DoString ("require 'main'"). Then, load other scripts in main.lua (like executing the Lua script's command line: lua main.lua).
Someone may ask: What should I do if my Lua file is downloaded, is extracted from a file in a custom format, or needs to be decrypted? Good question. XLua's custom loader can meet your needs.
3. Customized loaders
Its simple to customize loaders on xLua. Only one interface is involved:
public delegate byte[] CustomLoader(ref string filepath);
public void LuaEnv.AddLoader(CustomLoader loader)
With the AddLoader, you can register a callback. Its parameter is a string. When calling require in the Lua code, the parameter will be transparently transferred to the callback, which can load the specified files based on this parameter. If debugging support is required, you need to modify the filepath to a real path and transfer it. The returned value of this callback is a byte array. Null means the loader is not found. Otherwise, the content of the Lua file is returned.
Can IIPS IFS be loaded? Yes. Write a loader to call IIPS interface to read the content of the file. Can encrypted files be loaded? Yes, write the loader to read the file and return it after decrypting it.
See the complete example in the XLua\Tutorial\LoadLuaScript\Loader directory.
### C# accesses Lua.
This means that C# initiates access to the Lua data structure.
The examples mentioned in this chapter can be found in the XLua\Tutorial\CSharpCallLua directory.
1. Get a global basic data type
Access LuaEnv.Global, which provides a template Get method. You can specify the type returned.
luaenv.Global.Get<int>("a")
luaenv.Global.Get<string>("b")
luaenv.Global.Get<bool>("c")
2. Access a global table
What class should I specify if the above Get method also been used?
1. Map it to an ordinary class or struct.
Define a class, which has a public property of the field corresponding to table. Doing this either with or without a parameter constructor is OK. For example, for {f1 = 100, f2 = 100}, you can define a class that contains public int f1;public int f2;.
In this way, xLua will help you to create a new instance and set the corresponding fields to it.
Table properties can be more or less than class properties. You can nest other complex classes.
It should be noted that this process is to copy values. Complex classes will have higher overhead. And, modified field values of the class will not be synchronized to table, and vice versa.
This feature can reduce the generation overhead by adding the class to GCOptimize. For details, please see the configuration documentation.
Is mapping in the reference mode available? Yes, this is one method:
2. Map to an interface
This mode relies on the generated code. (If no code is generated, the InvalidCastException error will be reported.) The code generator will generate an instance of this interface. When getting a property, the generated code will get the corresponding table field; when setting a property, the generated code will also set the corresponding table field. You can even access Lua functions via the interface method.
3. Lightweight by value mode: map to Dictionary<>, List<>
If you do not want to define the type or interface, you can using this. The premise is that table key and value are of the same type.
4. Another methods are woking on ref mode: map to LuaTable class.
The advantage of this mode lies in that there is no need to generate code, but there are also some problems with this method. For example, it is an order of magnitude slower than mode 2, and there is no class checking.
3. Access a global function.
This still uses the Get method, but maps to a different class.
1. Map to a delegate
This is the recommended approach, with much better performance and higher class safety. The disadvantage of this method is the generated code. (If no code is generated, the InvalidCastException error will be reported.)
How do I declare a delegate?
For each parameter of the function, declare an input type parameter.
How do I deal with multiple returned values? Map to the C# output parameters from left to right. The output parameters include returned value, out parameter, and ref parameter.
What parameters and returned value types are supported? All are supported. A variety of complex types can be returned, including out, ref modified, and even another delegate.
Using a delegate is even simpler. It can be used just like a function.
2. Map to LuaFunction
The advantages and disadvantages of this approach are exactly the opposite of the first method.
Using this is also simple. LuaFunction has a Call function with variable parameters, and you can transfer any type and any number of parameters. The returned values are an array of objects, corresponding to Lua multiple returned values.
4. Usage suggestions
1. The overhead is high to access Lua global data, especially tables and functions. We recommended doing this as little as possible. For example, during initialization, get the Lua function to be called later (map to the delegate) and save it. Then, you can directly call the delegate. (Tables are similar)
2. If all implementations of Lua are in the delegate and an interface mode, their use can be completely decoupled from xLua. A dedicated module can be responsible for the initialization of xLua and the mapping of delegates and interfaces. Then, you can set these delegates and interfaces to where they will be used.
### Lua calls C#
> The examples covered in this section are all under XLua\Tutorial\LuaCallCSharp
#### Create a new C# object
You can create a new C# object this way:
var newGameObj = new UnityEngine.GameObject();
Make it correspond to Lua this way:
local newGameObj = CS.UnityEngine.GameObject()
These are basically the same, except in the following ways:
1. No new keyword is provided in Lua;
2. All C# related content is placed in the CS, including constructors, static member properties, and methods.
How do I deal with multiple constructors? No problem, xLua supports overloads. For example, if you want to call the constructor for GameObject with a string parameter, you can write it this way:
local newGameObj2 = CS.UnityEngine.GameObject('helloworld')
#### Access C# static properties and methods.
##### Read static properties
CS.UnityEngine.Time.deltaTime
##### Write static properties
CS.UnityEngine.Time.timeScale = 0.5
##### Call static methods
CS.UnityEngine.GameObject.Find('helloworld')
Tip: For the types you need to frequently access, you can reference them with local variables before calling them. This can reduce programing time and improve performance.
local GameObject = CS.UnityEngine.GameObject
GameObject.Find('helloworld')
#### Access C# member properties, methods
##### Read member properties
testobj.DMF
##### Write member properties
testobj.DMF = 1024
##### Call member methods
Note: When calling the member method, the first parameter needs to transfer this object. We recommended using the colon syntactic sugar as shown below:
testobj:DMFunc()
##### Parent properties and methods
XLua supports (via derived types) access to static properties and static methods of a base type, (via derived type instances) access to member properties, and member methods of base type.
##### Parameter input and output properties (out and ref)
Processing rules for parameters called by Lua: The ordinary parameter of C# is an input formal parameter. Ref modified is an input formal parameter, but out is not. The rest correspond from left to right to the actual parameter list called by Lua.
The processing rule for returned values called by Lua: The returned value of a C# function (if any) is a returned value, out is a returned value, and ref is a returned value. The rest correspond to multiple returned values of Lua from left to right.
##### Overload method:
This method allows you to access overloaded functions directly through different parameter types, for example:
testobj:TestFunc(100)
testobj:TestFunc('hello')
The integer parameter TestFunc and the string parameter TestFunc will be accessed separately.
Note: xLua only supports overloaded function calls to a certain extent. Because Lua supports far fewer types than C# does, there will be one-to-many situations. For example, C#'s int, float, and double types all correspond to Lua's number type. In the above example, if TestFunc has these overload parameters, the first line will not be able to distinguish between them and only one of them can be called (the first in the generated code).
##### Operators
These operators are supported: +, -, *, /, ==, unary-, <, <=, %[]
##### Methods whose parameters have default values:
This is the same as when C# calls a function with a default value. If the given actual parameters are less than the formal parameters, the default values will be added.
##### Variable parameter methods
For the following C# parameter:
void VariableParamsFunc(int a, params string[] strs)
You can call it in Lua this way:
testobj:VariableParamsFunc(5, 'hello', 'john')
##### Use extension methods
Lua can use these directly after you define them in C#.
##### Generic (template) methods
These are not directly supported, but you can call them after packaging them through the Extension methods feature.
##### The Enumerated Type
Enumerated values are just like static properties of the enumerated type.
testobj:EnumTestFunc(CS.Tutorial.TestEnum.E1)
The EnumTestFunc function parameter shown above is the Tutorial.TestEnum type.
Enum has a __CastFrom method. This implements conversion from an integer or string to an enumerated value. For example:
CS.Tutorial.TestEnum.__CastFrom(1)
CS.Tutorial.TestEnum.__CastFrom('E1')
##### Delegate use (call, +, -)
Call C# delegate: This is the same as calling the ordinary Lua function.
+ operator: This corresponds to the C# + operator. It combines two calls into a call chain, and the right operand can be of the same type as the C# delegate or Lua function.
- operator: In contrast to +, this removes a delegate from the call chain.
> PS: The delegate property can be set with a Lua function.
##### Events:
For example, testobj has the following event definition: public event Action TestEvent;
Add event callbacks
testobj:TestEvent('+', lua_event_callback)
Remove event callbacks
testobj:TestEvent('-', lua_event_callback)
##### Support for 64-bit integers
In Lua version 5.3, 64-bit integers (long, ulong) are mapped to native 64-bit integers. Since the LuaJIT version (equivalent to the standard Lua 5.1 version ) does not support 64-bit integers, xLua provides a 64-bit support extension library. Both C# long and ulong integers will be mapped to userdata.
64-bit operations, comparisons, print
support, and Lua number operations are supported in Lua. It should be noted for comparison
that in the 64 extension library, only int64 and ulong integers will be strongly converted to long integers first and then transferred to Lua. In some of Ulong's operations, for the sake of comparison, we use the same support mode as Java: providing a set of APIs. For details, see the API documentation.
##### Automatic conversion between C# complex types and tables
For a C# complex type with no parameter constructor, a table can be used directly as a substitute on Lua. The table corresponds to a public field of a complex type. It supports function parameter transfer, property assignment, etc. For example:
The definition of B structure (type also supported) on C# is as follows:
public struct A
{
public int a;
}
public struct B
{
public A b;
public double c;
}
A type has this member function:
void Foo(B b)
Lua can call it in this way:
obj:Foo({b = {a = 100}, c = 200})
##### Get type (equivalent to C#'s typeof)
For example, you can get the type information of the UnityEngine.ParticleSystem type this way:
typeof(CS.UnityEngine.ParticleSystem)
##### "Strong" conversion
Lua is not a typed language, so it has no "strong" conversion with strongly typed languages. However, there's something similar: tell xLua to call an object with the specified generated code. Under what circumstances will it be used? The answer is that, sometimes third-party libraries expose an interface or an abstract type. The implementation type is hidden, so we cannot generate code for the implementation type. This implementation type will be identified by xLua as the ungenerated code and accessed via reflection. Frequently calling it will affect performance significantly. We can add this interface or abstract type to the generated code, and then specify using the generated code to access objects:
cast(calc, typeof(CS.Tutorial.Calc))
The above example specifies using the generated code of CS.Tutorial.Calc to access the calc object.
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 76e3df78477813b47bb62712878a2f62
timeCreated: 1529661499
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 40e0633beaaf4dd49aca86e58539a814
timeCreated: 1469709930
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,128 @@
## What&Why
XLua目前内置的扩展库:
* 针对luajit的64位整数支持;
* 函数调用耗时以及内存泄漏定位工具;
* 用于支持ZeroBraneStudio的luasocket库;
* tdr 4 lua
随着使用项目的增加以及项目使用的深入程度,仅有这几个扩展已经没法满足项目组了,而由于各个项目对扩展差异化比较大,以及手机平台对安装包大小的敏感,XLua是无法通过预集成去满足这些需求,这也是这篇教程的由来。
这篇教程,将以lua-rapidjson为例,一步步的讲述怎么往xLua添加c/c++扩展,当然,会添加了,自然删除也就会了,项目组可以自行删除不需要用到的预集成扩展。
## How
分三步
1. 修改build文件、工程设置,把要集成的扩展编译到XLua Plugin里头;
2. 调用xLua的C# API,使得扩展可以被按需(在lua代码里头require的时候)加载;
3. 可选,如果你的扩展里头需要用到64位整数,你可以通过XLua的64位扩展库来实现和C#的配合
### 一、添加扩展&编译
准备工作
1. 把xLua的C源码包解压到你Unity工程的Assets同级目录下。
下载lua-rapidjson代码,按你的习惯放置。本教程是把rapidjson头文件放到$UnityProj\build\lua-rapidjson\include目录下,而扩展的源码rapidjson.cpp放到$UnityProj\build\lua-rapidjson\source目录下(注:$UnityProj指的是你工程的目录)
2. 在CMakeLists.txt加入扩展
xLua的各平台Plugins编译使用cmake编译,好处是所有平台的编译都写在一个makefile,大部分编译处理逻辑是跨平台的。
xLua配套的CMakeLists.txt为第三方扩展提供了扩展点(都是list):
1. THIRDPART_INC:第三方扩展的头文件搜索路径。
2. THIRDPART_SRC:第三方扩展的源代码。
3. THIRDPART_LIB:第三方扩展依赖的库。
如下是rapidjson的加法
#begin lua-rapidjson
set (RAPIDJSON_SRC lua-rapidjson/source/rapidjson.cpp)
set_property(
SOURCE ${RAPIDJSON_SRC}
APPEND
PROPERTY COMPILE_DEFINITIONS
LUA_LIB
)
list(APPEND THIRDPART_INC lua-rapidjson/include)
set (THIRDPART_SRC ${THIRDPART_SRC} ${RAPIDJSON_SRC})
#end lua-rapidjson
完整代码请见附件。
3. 各平台编译
所有编译脚本都是按这个方式命名:make_平台_lua版本.后缀。
比如windows 64位lua53版本是make_win64_lua53.batandroid的luajit版本是make_android_luajit.sh,要编译哪个版本就执行相应的脚本即可。
执行完编译脚本会自动拷贝到plugin_lua53或者plugin_luajit目录,前者是lua53版本放置路径,后者是luajit。
配套的android脚本是在linux下使用的,脚本开头的NDK路径要根据实际情况修改。
### 二、C#侧集成
所有lua的C扩展库都会提供个luaopen_xxx的函数,xxx是动态库的名字,比如lua-rapidjson库该函数是luaopen_rapidjson,这类函数由lua虚拟机在加载动态库时自动调用,而在手机平台,由于ios的限制我们加载不了动态库,而是直接编译进进程里头。
为此,XLua提供了一个API来替代这功能(LuaEnv的成员方法):
public void AddBuildin(string name, LuaCSFunction initer)
参数:
namebuildin模块的名字,require时输入的参数;
initer:初始化函数,原型是这样的public delegate int lua_CSFunction(IntPtr L),必须是静态函数,而且带MonoPInvokeCallbackAttribute属性修饰,这个api会检查这两个条件。
我们以luaopen_rapidjson的调用来看看怎么使用。
扩展LuaDLL.Lua类,用pinvoke把luaopen_rapidjson导出到C#,然后写一个符合lua_CSFunction定义的静态函数,你可以在里头做写初始化工作,比如luaopen_rapidjson的调用,以下是完整代码:
namespace LuaDLL
{
public partial class Lua
{
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int luaopen_rapidjson(System.IntPtr L);
[MonoPInvokeCallback(typeof(LuaDLL.lua_CSFunction))]
public static int LoadRapidJson(System.IntPtr L)
{
return luaopen_rapidjson(L);
}
}
}
然后调用AddBuildin
luaenv.AddBuildin("rapidjson", LuaDLL.Lua.LoadRapidJson);
然后就ok了,在lua代码中试试该扩展:
local rapidjson = require('rapidjson')
local t = rapidjson.decode('{"a":123}')
print(t.a)
t.a = 456
local s = rapidjson.encode(t)
print('json', s)
### 三、64位改造
把i64lib.h文件include到需要64位改造的文件里头。
该头文件的API就以下几个:
//往栈上放一个int64/uint64
void lua_pushint64(lua_State* L, int64_t n);
void lua_pushuint64(lua_State* L, uint64_t n);
//判断栈上pos位置是否是int64/uint64
int lua_isint64(lua_State* L, int pos);
int lua_isuint64(lua_State* L, int pos);
//从栈上pos位置取一个int64/uint64
int64_t lua_toint64(lua_State* L, int pos);
uint64_t lua_touint64(lua_State* L, int pos);
这些API的使用依情况而定,可以看看本文附带的附件(rapidjson.cpp文件)
编译工程相关修改
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6a5e50ce3ca4b6140858bd4eb6efc2e9
timeCreated: 1518577405
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 11d97e567c78f3147b86070c103f3d2d
timeCreated: 1472455442
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,14 @@
## 复杂值类型的gc问题
xLua复杂值类型(struct)的默认传递方式是引用传递,这种方式要求先对值类型boxing,传递给lua,lua使用后释放该引用。由于值类型每次boxing将产生一个新对象,当lua侧使用完毕释放该对象的引用时,则产生一次gc。
为此,xLua实现了一套struct的gc优化方案,您只要通过简单的配置,则可以实现满足条件的struct传递到lua侧无gc。
## struct需要满足什么条件?
1. struct允许嵌套其它struct,但它以及它嵌套的struct只能包含这几种基本类型:byte、sbyte、short、ushort、int、uint、long、ulong、float、double;例如UnityEngine定义的大多数值类型:Vector系列,QuaternionColor。。。均满足条件,或者用户自定义的一些struct
2. 该struct配置了GCOptimize属性(对于常用的UnityEngine的几个structVector系列,QuaternionColor。。。均已经配置了该属性),这个属性可以通过配置文件或者C# Attribute实现;
3. 使用到该struct的地方,需要添加到生成代码列表;
## 如何配置?
`XLua的配置.md`的GCOptimize章节。
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 688574fb2ae996243b1cb02ec3a78bee
timeCreated: 1518577405
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5e83c211c4c43834b9c8027b7480ab5f
timeCreated: 1462265117
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,36 @@
XLua内置两个小工具进行性能方面问题的分析:一个是Lua函数,Lua调用C#函数的时长(不一定等同于CPU耗时,比如协程yield出去那段时间也会被算入调用时间)分析工具;一个是内存泄漏定位工具。
## 函数调用时长分析工具
### 典型使用案例:
说明:
api很简单,就三个,start和stop都是无参数,也很好理解,分别是统计开始以及结束。在start以及stop之间可以多次调用report(也可以考虑不调stop)。每次report会得到从start到调用report为止的函数时长统计报告(以字符串返回)。report函数只有一个可选参数,可以指明按照总时间(参数是字符串的”TOTAL”,这个是默认值),平均每次调用时间(“AVERAGE”),以及调用次数(“CALLED”)来排序。
典型的一个时长统计报告如下:
第一列是函数名
第二列是源代码,如果是lua文件将会统计到文件,行号,如果是C#的导出代码,将会标注[C#],如果是C函数,标准为[C]。
后面几列分别是总时间,平均每次调用时间,占总统计时间的百分比,以及调用次数。
## 内存泄漏定位工具
### 典型使用案例:
说明:
api就两个,total获取lua虚拟机的内存占用,单位是Kbytes,以lua number返回。而snapshot返回当前内存快照信息,一个典型的快照报告如下:
第一列是table变量名;
第二列是table的大小,如果该table下有子table也会被统计到;
第三列是变量的类型,UPVALUE代表是闭包里头的变量(内层函数对外层local变量的引用),GLOBAL是全局变量,REGISTRY是C侧(包含虚拟机内部的)的一些私有数据。一般主要关注前面两种;
第四列是变量的ID,其实就是内存指针,如果两次报告间没被重新分配,该ID是不变的,便于识别是否同一table;
最后一列是一些附加信息,比如闭包变量可能存在很多同名的,这里会通过有那些函数引用了该变量来协助定位。
如何定位内存泄漏:通过memory.total检测出内存的持续增长,然后通过memory.snapshot定位出哪里泄漏(lua测的内存泄漏都是表现为往某table添加了数据忘了删除)。
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f8b5c3f531c153c409bb336ba21682ab
timeCreated: 1518577405
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9678cc58c9b40e147b514f7f5122ee20
timeCreated: 1456291064
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,290 @@
## xLua教程
### Lua文件加载
1. 执行字符串
最基本是直接用LuaEnv.DoString执行一个字符串,当然,字符串得符合Lua语法
比如:
luaenv.DoString("print('hello world')")
完整代码见XLua\Tutorial\LoadLuaScript\ByString目录
> 但这种方式并不建议,更建议下面介绍这种方法。
2. 加载Lua文件
用lua的require函数即可
比如:
DoString("require 'byfile'")
完整代码见XLua\Tutorial\LoadLuaScript\ByFile目录
require实际上是调一个个的loader去加载,有一个成功就不再往下尝试,全失败则报文件找不到。
目前xLua除了原生的loader外,还添加了从Resource加载的loader,需要注意的是因为Resource只支持有限的后缀,放Resources下的lua文件得加上txt后缀(见附带的例子)。
建议的加载Lua脚本方式是:整个程序就一个DoString("require 'main'"),然后在main.lua加载其它脚本(类似lua脚本的命令行执行:lua main.lua)。
有童鞋会问:要是我的Lua文件是下载回来的,或者某个自定义的文件格式里头解压出来,或者需要解密等等,怎么办?问得好,xLua的自定义Loader可以满足这些需求。
3. 自定义Loader
在xLua加自定义loader是很简单的,只涉及到一个接口:
public delegate byte[] CustomLoader(ref string filepath);
public void LuaEnv.AddLoader(CustomLoader loader)
通过AddLoader可以注册个回调,该回调参数是字符串,lua代码里头调用require时,参数将会透传给回调,回调中就可以根据这个参数去加载指定文件,如果需要支持调试,需要把filepath修改为真实路径传出。该回调返回值是一个byte数组,如果为空表示该loader找不到,否则则为lua文件的内容。
有了这个就简单了,用IIPS的IFS?没问题。写个loader调用IIPS的接口读文件内容即可。文件已经加密?没问题,自己写loader读取文件解密后返回即可。。。
完整示例见XLua\Tutorial\LoadLuaScript\Loader
### C#访问Lua
这里指的是C#主动发起对Lua数据结构的访问
本章涉及到的例子都可以在XLua\Tutorial\CSharpCallLua下找到。
1. 获取一个全局基本数据类型
访问LuaEnv.Global就可以了,上面有个模版Get方法,可指定返回的类型。
luaenv.Global.Get<int>("a")
luaenv.Global.Get<string>("b")
luaenv.Global.Get<bool>("c")
2. 访问一个全局的table
也是用上面的Get方法,那类型要指定成啥呢?
1. 映射到普通class或struct
定义一个class,有对应于table的字段的public属性,而且有无参数构造函数即可,比如对于{f1 = 100, f2 = 100}可以定义一个包含public int f1;public int f2;的class。
这种方式下xLua会帮你new一个实例,并把对应的字段赋值过去。
table的属性可以多于或者少于class的属性。可以嵌套其它复杂类型。
要注意的是,这个过程是值拷贝,如果class比较复杂代价会比较大。而且修改class的字段值不会同步到table,反过来也不会。
这个功能可以通过把类型加到GCOptimize生成降低开销,详细可参见配置介绍文档。
那有没有引用方式的映射呢?有,下面这个就是:
2. 映射到一个interface
这种方式依赖于生成代码(如果没生成代码会抛InvalidCastException异常),代码生成器会生成这个interface的实例,如果get一个属性,生成代码会get对应的table字段,如果set属性也会设置对应的字段。甚至可以通过interface的方法访问lua的函数。
3. 更轻量级的by value方式:映射到Dictionary<>List<>
不想定义class或者interface的话,可以考虑用这个,前提table下key和value的类型都是一致的。
4. 另外一种by ref方式:映射到LuaTable类
这种方式好处是不需要生成代码,但也有一些问题,比如慢,比方式2要慢一个数量级,比如没有类型检查。
3. 访问一个全局的function
仍然是用Get方法,不同的是类型映射。
1. 映射到delegate
这种是建议的方式,性能好很多,而且类型安全。缺点是要生成代码(如果没生成代码会抛InvalidCastException异常)。
delegate要怎样声明呢?
对于function的每个参数就声明一个输入类型的参数。
多返回值要怎么处理?从左往右映射到c#的输出参数,输出参数包括返回值,out参数,ref参数。
参数、返回值类型支持哪些呢?都支持,各种复杂类型,out,ref修饰的,甚至可以返回另外一个delegate。
delegate的使用就更简单了,直接像个函数那样用就可以了。
2. 映射到LuaFunction
这种方式的优缺点刚好和第一种相反。
使用也简单,LuaFunction上有个变参的Call函数,可以传任意类型,任意个数的参数,返回值是object的数组,对应于lua的多返回值。
4. 使用建议
1. 访问lua全局数据,特别是table以及function,代价比较大,建议尽量少做,比如在初始化时把要调用的lua function获取一次(映射到delegate)后,保存下来,后续直接调用该delegate即可。table也类似。
2. 如果lua侧的实现的部分都以delegate和interface的方式提供,使用方可以完全和xLua解耦:由一个专门的模块负责xlua的初始化以及delegate、interface的映射,然后把这些delegate和interface设置到要用到它们的地方。
### Lua调用C#
> 本章节涉及到的实例均在XLua\Tutorial\LuaCallCSharp下
#### new C#对象
你在C#这样new一个对象
var newGameObj = new UnityEngine.GameObject();
对应到Lua是这样:
local newGameObj = CS.UnityEngine.GameObject()
基本类似,除了:
1. lua里头没有new关键字;
2. 所有C#相关的都放到CS下,包括构造函数,静态成员属性、方法;
如果有多个构造函数呢?放心,xlua支持重载,比如你要调用GameObject的带一个string参数的构造函数,这么写:
local newGameObj2 = CS.UnityEngine.GameObject('helloworld')
#### 访问C#静态属性,方法
##### 读静态属性
CS.UnityEngine.Time.deltaTime
##### 写静态属性
CS.UnityEngine.Time.timeScale = 0.5
##### 调用静态方法
CS.UnityEngine.GameObject.Find('helloworld')
小技巧:如果需要经常访问的类,可以先用局部变量引用后访问,除了减少敲代码的时间,还能提高性能:
local GameObject = CS.UnityEngine.GameObject
GameObject.Find('helloworld')
#### 访问C#成员属性,方法
##### 读成员属性
testobj.DMF
##### 写成员属性
testobj.DMF = 1024
##### 调用成员方法
注意:调用成员方法,第一个参数需要传该对象,建议用冒号语法糖,如下
testobj:DMFunc()
##### 父类属性,方法
xlua支持(通过派生类)访问基类的静态属性,静态方法,(通过派生类实例)访问基类的成员属性,成员方法
##### 参数的输入输出属性(out,ref)
Lua调用侧的参数处理规则:C#的普通参数算一个输入形参,ref修饰的算一个输入形参,out不算,然后从左往右对应lua 调用侧的实参列表;
Lua调用侧的返回值处理规则:C#函数的返回值(如果有的话)算一个返回值,out算一个返回值,ref算一个返回值,然后从左往右对应lua的多返回值。
##### 重载方法
直接通过不同的参数类型进行重载函数的访问,例如:
testobj:TestFunc(100)
testobj:TestFunc('hello')
将分别访问整数参数的TestFunc和字符串参数的TestFunc。
注意:xlua只一定程度上支持重载函数的调用,因为lua的类型远远不如C#丰富,存在一对多的情况,比如C#的intfloatdouble都对应于lua的number,上面的例子中TestFunc如果有这些重载参数,第一行将无法区分开来,只能调用到其中一个(生成代码中排前面的那个)
##### 操作符
支持的操作符有:+,-,*,/,==,一元-<<= %[]
##### 参数带默认值的方法
和C#调用有默认值参数的函数一样,如果所给的实参少于形参,则会用默认值补上。
##### 可变参数方法
对于C#的如下方法
void VariableParamsFunc(int a, params string[] strs)
可以在lua里头这样调用:
testobj:VariableParamsFunc(5, 'hello', 'john')
##### 使用Extension methods
在C#里定义了lua里就能直接使用。
##### 泛化(模版)方法
不直接支持,可以通过Extension methods功能进行封装后调用。
##### 枚举类型
枚举值就像枚举类型下的静态属性一样。
testobj:EnumTestFunc(CS.Tutorial.TestEnum.E1)
上面的EnumTestFunc函数参数是Tutorial.TestEnum类型的。
枚举类支持__CastFrom方法,可以实现从一个整数或者字符串到枚举值的转换,例如:
CS.Tutorial.TestEnum.__CastFrom(1)
CS.Tutorial.TestEnum.__CastFrom('E1')
##### delegate使用(调用,+-
C#的delegate调用:和调用普通lua函数一样
+操作符:对应C#的+操作符,把两个调用串成一个调用链,右操作数可以是同类型的C# delegate或者是lua函数。
-操作符:和+相反,把一个delegate从调用链中移除。
> Psdelegate属性可以用一个luafunction来赋值。
##### event
比如testobj里头有个事件定义是这样:public event Action TestEvent;
增加事件回调
testobj:TestEvent('+', lua_event_callback)
移除事件回调
testobj:TestEvent('-', lua_event_callback)
##### 64位整数支持
Lua53版本64位整数(long,ulong)映射到原生的64位整数,而luajit版本,相当于lua5.1的标准,本身不支持64位,xlua做了个64位支持的扩展库,C#的long和ulong都将映射到userdata
支持在lua里头进行64位的运算,比较,打印
支持和lua number的运算,比较
要注意的是,在64扩展库中,实际上只有int64,ulong也会先强转成long再传递到lua,而对ulong的一些运算,比较,我们采取和java一样的支持方式,提供一组API,详情请看API文档。
##### C#复杂类型和table的自动转换
对于一个有无参构造函数的C#复杂类型,在lua侧可以直接用一个table来代替,该table对应复杂类型的public字段有相应字段即可,支持函数参数传递,属性赋值等,例如:
C#下B结构体class也支持)定义如下:
public struct A
{
public int a;
}
public struct B
{
public A b;
public double c;
}
某个类有成员函数如下:
void Foo(B b)
在lua可以这么调用
obj:Foo({b = {a = 100}, c = 200})
##### 获取类型(相当于C#的typeof
比如要获取UnityEngine.ParticleSystem类的Type信息,可以这样
typeof(CS.UnityEngine.ParticleSystem)
##### “强”转
lua没类型,所以不会有强类型语言的“强转”,但有个有点像的东西:告诉xlua要用指定的生成代码去调用一个对象,这在什么情况下能用到呢?有的时候第三方库对外暴露的是一个interface或者抽象类,实现类是隐藏的,这样我们无法对实现类进行代码生成。该实现类将会被xlua识别为未生成代码而用反射来访问,如果这个调用是很频繁的话还是很影响性能的,这时我们就可以把这个interface或者抽象类加到生成代码,然后指定用该生成代码来访问:
cast(calc, typeof(CS.Tutorial.Calc))
上面就是指定用CS.Tutorial.Calc的生成代码来访问calc对象。
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9d871df7bbc973d468622dd10b6f7ea9
timeCreated: 1518577405
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 57c6cf634c35eb146b95206d498cbf99
timeCreated: 1480488641
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,41 @@
# 通用字节码
不少项目希望把项目的lua源码通过luac编译后加载,但官方lua有个缺陷:字节码是分32位和64位版本的,换句话你32位lua环境只能跑32位luac编译出来的东西。
为此,xLua尝试对lua源码做了少许改造,可以编译一份字节码,跨平台使用。
## 注意事项
* 1、如果你做了本文所描述的改动,你的xLua将加载不了官方luac所编译的字节码;
* 2、截至2018/9/14,已知此改法在一个上线一个多月的项目正常运行,但不代表此改法在任何情况都没问题。
## 操作指南
### 1、编译xlua的Plugins
修改各平台编译脚本,在cmake命令加上-DLUAC_COMPATIBLE_FORMAT=ON参数,以make_win64_lua53.bat为例,修改后是这样的:
~~~bash
mkdir build64 & pushd build64
cmake -DLUAC_COMPATIBLE_FORMAT=ON -G "Visual Studio 14 2015 Win64" ..
popd
cmake --build build64 --config Release
md plugin_lua53\Plugins\x86_64
copy /Y build64\Release\xlua.dll plugin_lua53\Plugins\x86_64\xlua.dll
pause
~~~
用修改后的编译脚本重新编译各平台的xlua库,并覆盖原Plugins目录下对应文件。
## 2、编译能生成兼容格式的luac(后续只能用这特定的luac和步骤1的Plugins配套使用)
到[这里](../../../build/luac/),如果你想编译window版本的,执行make_win64.bat,如果你要编译mac或者linux的,用make_unix.sh
## 3、加载字节码
通过CustomLoader加载即可,CustomLoader的详细情况请看教程。这个步骤常犯的错误是用某种Encoding去加载二进制文件,这会破坏lua字节码文件格式。谨记得以二进制方式加载。
## PS: OpCode修改
有项目想修改为专用格式的字节码,直接在lua源码(目前是lua-5.3.5)上修改后,重新执行上述1、2操作步骤即可。
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9ace0cd26c96dc6468f5e054d8168b43
timeCreated: 1536904183
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,142 @@
# xLua的配置
xLua所有的配置都支持三种方式:打标签;静态列表;动态列表。
配置有两必须两建议:
* 列表方式均必须是static的字段/属性
* 列表方式均必须放到一个static类
* 建议不用标签方式
* 建议列表方式配置放Editor目录(如果是Hotfix配置,而且类位于Assembly-CSharp.dll之外的其它dll,必须放Editor目录)
**打标签**
xLua用白名单来指明生成哪些代码,而白名单通过attribute来配置,比如你想从lua调用c#的某个类,希望生成适配代码,你可以为这个类型打一个LuaCallCSharp标签:
~~~csharp
[LuaCallCSharp]
publicclassA
{
}
~~~
该方式方便,但在il2cpp下会增加不少的代码量,不建议使用。
**静态列表**
有时我们无法直接给一个类型打标签,比如系统api,没源码的库,或者实例化的泛化类型,这时你可以在一个静态类里声明一个静态字段,该字段的类型除BlackList和AdditionalProperties之外只要实现了IEnumerable&lt;Type&gt;就可以了(这两个例外后面具体会说),然后为这字段加上标签:
~~~csharp
[LuaCallCSharp]
public static List<Type> mymodule_lua_call_cs_list = new List<Type>()
{
typeof(GameObject),
typeof(Dictionary<string, int>),
};
~~~
这个字段需要放到一个 **静态类** 里头,建议放到 **Editor目录**
**动态列表**
声明一个静态属性,打上相应的标签即可。
~~~csharp
[Hotfix]
public static List<Type> by_property
{
get
{
return (from type in Assembly.Load("Assembly-CSharp").GetTypes()
where type.Namespace == "XXXX"
select type).ToList();
}
}
~~~
Getter是代码,你可以实现很多效果,比如按名字空间配置,按程序集配置等等。
这个属性需要放到一个 **静态类** 里头,建议放到 **Editor目录**
### XLua.LuaCallCSharp
一个C#类型加了这个配置,xLua会生成这个类型的适配代码(包括构造该类型实例,访问其成员属性、方法,静态属性、方法),否则将会尝试用性能较低的反射方式来访问。
一个类型的扩展方法(Extension Methods)加了这配置,也会生成适配代码并追加到被扩展类型的成员方法上。
xLua只会生成加了该配置的类型,不会自动生成其父类的适配代码,当访问子类对象的父类方法,如果该父类加了LuaCallCSharp配置,则执行父类的适配代码,否则会尝试用反射来访问。
反射访问除了性能不佳之外,在il2cpp下还有可能因为代码剪裁而导致无法访问,后者可以通过下面介绍的ReflectionUse标签来避免。
### XLua.ReflectionUse
一个C#类型类型加了这个配置xLua会生成link.xml阻止il2cpp的代码剪裁。
对于扩展方法,必须加上LuaCallCSharp或者ReflectionUse才可以被访问到。
建议所有要在Lua访问的类型,要么加LuaCallCSharp,要么加上ReflectionUse,这才能够保证在各平台都能正常运行。
### XLua.DoNotGen
指明一个类里头的部分函数、字段、属性不生成代码,通过反射访问。
只能标准Dictionary<Type, List<string>>的field或者property。key指明的是生效的类,value是一个列表,配置的是不生成代码的函数、字段、属性的名字。
和ReflectionUse的区别是:1、ReflectionUse指明的是整个类;2、当第一次访问一个函数(字段、属性)时,ReflectionUse会把整个类都wrap,而DoNotGen只wrap该函数(字段、属性),换句话DoNotGen更lazy一些;
和BlackList的区别是:1、BlackList配了就不能用;2、BlackList能指明某重载函数,DoNotGen不能;
### XLua.CSharpCallLua
如果希望把一个lua函数适配到一个C# delegate(一类是C#侧各种回调UI事件,delegate参数,比如List&lt;T&gt;:ForEach;另外一类场景是通过LuaTable的Get函数指明一个lua函数绑定到一个delegate)。或者把一个lua table适配到一个C# interface,该delegate或者interface需要加上该配置。
### XLua.GCOptimize
一个C#纯值类型(注:指的是一个只包含值类型的struct,可以嵌套其它只包含值类型的struct)或者C#枚举值加上了这个配置。xLua会为该类型生成gc优化代码,效果是该值类型在lua和c#间传递不产生(C#)gc alloc,该类型的数组访问也不产生gc。各种无GC的场景,可以参考05\_NoGc例子。
除枚举之外,包含无参构造函数的复杂类型,都会生成lua table到该类型,以及改类型的一维数组的转换代码,这将会优化这个转换的性能,包括更少的gc alloc。
### XLua.AdditionalProperties
这个是GCOptimize的扩展配置,有的时候,一些struct喜欢把field做成是私有的,通过property来访问field,这时就需要用到该配置(默认情况下GCOptimize只对public的field打解包)。
标签方式比较简单,配置方式复杂一点,要求是Dictionary&lt;Type, List&lt;string&gt;&gt;类型,Dictionary的Key是要生效的类型,Value是属性名列表。可以参考XLua对几个UnityEngine下值类型的配置,SysGCOptimize类。
### XLua.BlackList
如果你不要生成一个类型的一些成员的适配代码,你可以通过这个配置来实现。
标签方式比较简单,对应的成员上加就可以了。
由于考虑到有可能需要把重载函数的其中一个重载列入黑名单,配置方式比较复杂,类型是List&lt;List&lt;string&gt;&gt;,对于每个成员,在第一层List有一个条目,第二层List是个string的列表,第一个string是类型的全路径名,第二个string是成员名,如果成员是一个方法,还需要从第三个string开始,把其参数的类型全路径全列出来。
例如下面是对GameObject的一个属性以及FileInfo的一个方法列入黑名单:
~~~csharp
[BlackList]
public static List<List<string>> BlackList = new List<List<string>>() {
new List<string>(){"UnityEngine.GameObject", "networkView"},
//new List<string>(){ typeof(UnityEngine.GameObject).FullName, "networkView"},
new List<string>(){"System.IO.FileInfo", "GetAccessControl", "System.Security.AccessControl.AccessControlSections"},
//new List<string>(){ typeof(System.IO.FileInfo).FullName, "GetAccessControl",typeof(System.Security.AccessControl.AccessControlSections).FullName },
};
~~~
### 下面是生成期配置,必须放到Editor目录下
### CSObjectWrapEditor.GenPath
配置生成代码的放置路径,类型是string。默认放在&quot;Assets/XLua/Gen/&quot;下。
### CSObjectWrapEditor.GenCodeMenu
该配置用于生成引擎的二次开发,一个无参数函数加了这个标签,在执行&quot;XLua/Generate Code&quot;菜单时会触发这个函数的调用。
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8e8a250c7dbdf6b48ae79313470374dc
timeCreated: 1498554474
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,109 @@
## 生成引擎二次开发
xLua的生成引擎支持二次开发,你可以利用它来生成一些文本类型的文件(比如代码,配置等)。xLua本身的link.xml文件的生成就是一个生成引擎插件做的。其它应用场景,比如生成Lua IDE的自动完成配置文件,都可以用这特性来完成。
## 总体介绍
插件需要提供两个东西:1、生成文件的模版;2、一个回调函数,该回调函数接受用户的配置,返回需要注入到模版的数据以及文件的输出流。
## 模版语法
模版语法很简单,只有三种元素:
* eval:语法是<%=exp%>,exp是任意表达式,将计算并以字符串形式输出exp的值;
* code:语法是<% if true then end%>,蓝色部分是任意lua代码,这些代码会执行;
* literal:除eval和code之外其它部分,literal原样输出。
示例:
~~~xml
<%
require "TemplateCommon"
%>
<linker>
<%ForEachCsList(assembly_infos, function(assembly_info)%>
<assembly fullname="<%=assembly_info.FullName%>">
<%ForEachCsList(assembly_info.Types, function(type)
%><type fullname="<%=type:ToString()%>" preserve="all"/>
<%end)%>
</assembly>
<%end)%>
</linker>
~~~
TemplateCommon有一些预定义的函数可以使用,比如ForEachCsList,可以搜索下工程的TemplateCommon.lua.txt看下有那些函数可以用,就普通的lua而已,你自己写一套也可以。
## API
~~~csharp
public static void CSObjectWrapEditor.Generator.CustomGen(string template_src, GetTasks get_tasks)
~~~
* template_src 模版的源码;
* get_tasks 回调函数,类型是GetTasks,用来接受用户的配置,返回需要注入到模版的数据以及文件的输出流;
~~~csharp
public delegate IEnumerable<CustomGenTask> GetTasks(LuaEnv lua_env, UserConfig user_cfg);
~~~
* lua_env LuaEnv对象,因为返回的模版数据需要放到LuaTable,需要用到LuaEnv.NewTable
* user_cfg 用户的配置;
* return 返回值中,CustomGenTask代表的是一个生成文件,而IEnumerable类型表示同一个模版可以生成多个文件;
~~~csharp
public struct UserConfig
{
public IEnumerable<Type> LuaCallCSharp;
public IEnumerable<Type> CSharpCallLua;
public IEnumerable<Type> ReflectionUse;
}
~~~
~~~csharp
public struct CustomGenTask
{
public LuaTable Data;
public TextWriter Output;
}
~~~
示例:
~~~csharp
public static IEnumerable<CustomGenTask> GetTasks(LuaEnv lua_env, UserConfig user_cfg)
{
LuaTable data = lua_env.NewTable();
var assembly_infos = (from type in user_cfg.ReflectionUse
group type by type.Assembly.GetName().Name into assembly_info
select new { FullName = assembly_info.Key, Types = assembly_info.ToList()}).ToList();
data.Set("assembly_infos", assembly_infos);
yield return new CustomGenTask
{
Data = data,
Output = new StreamWriter(GeneratorConfig.common_path + "/link.xml",
false, Encoding.UTF8)
};
}
~~~
* 这里只生成一个文件,故只返回一个CustomGenTask
* data就是模版要使用的数据,这里塞了一个assembly_infos字段,这个字段如何使用可以回头看看模版部分;
## 标签
一般来说你可以通过MenuItem开一个菜单来执行触发自定义生成操作,但有时你希望生成操作直接由xLua的“Generate Code”菜单触发,你就需要用到CSObjectWrapEditor.GenCodeMenu
示例:
~~~csharp
[GenCodeMenu]//加到Generate Code菜单里头
public static void GenLinkXml()
{
Generator.CustomGen(ScriptableObject.CreateInstance<LinkXmlGen>().Template.text, GetTasks);
}
~~~
ps:以上所有相关代码都在XLua\Src\Editor\LinkXmlGen目录下,也正是文章开头说的link.xml的生成功能的实现。
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6436d38981b6f5a4d8a2255ea3145ed1
timeCreated: 1486519283
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
+528
View File
@@ -0,0 +1,528 @@
# FAQ
## xLua发布包怎么用?
xLua目前以zip包形式发布,在工程目录下解压即可。
## xLua可以放别的目录吗?
可以,但生成代码目录需要配置一下(默认放Assets\XLua\Gen目录),具体可以看《XLua的配置.doc》的GenPath配置介绍。
更改目录要注意的是:生成代码和xLua核心代码必须在同一程序集。如果你要用热补丁特性,xLua核心代码必须在Assembly-CSharp程序集。
## xLua的配置
如果你用的是lua编程,你可能需要把一些频繁访问的类配置到LuaCallCSharp,这些类的成员如果有条件编译(大多数情况下是UNITY_EDITOR)的话,你需要通过BlackList配置排除;如果你需要通过delegate回调到lua的地方,得把这些delegate配置到CSharpCallLua。
如果你用的是热补丁,你需要把要注入的代码加到Hotfix列表;如果你需要通过delegate回调到lua的地方,也得把这些delegate配置到CSharpCallLua。
xLua提供了强大的动态配置,让你可以结合反射实现任意的自动化配置,动态配置介绍看[这里](configure.md)。xLua希望你能根据自身项目的需求自行配置,同时为了方便部分对反射api了解不够的童鞋,xLua也针对上面两者方式分别写了参考配置:[ExampleConfig.cs](../Editor/ExampleConfig.cs),直接打开相应部分的注释即可使用。
## lua源码只能以txt后缀?
什么后缀都可以。
如果你想以TextAsset打包到安装包(比如放到Resources目录),Unity不认lua后缀,这是Unity的规则。
如果你不打包到安装包,就没有后缀的限制:比如自行下载到某个目录(这也是热更的正确姿势),然后通过CustomLoader或者设置package.path去读这个目录。
那为啥xLua本身带的lua源码(包括示例)为什么都是txt结尾呢?因为xLua本身就一个库,不含下载功能,也不方便运行时去某个地方下载代码,通过TextAsset是较简单的方式。
## 编辑器(或非il2cpp的android)下运行正常,ios下运行调用某函数报“attempt to call a nil value”
il2cpp默认会对诸如引擎、c#系统api,第三方dll等等进行代码剪裁。简单来说就是这些地方的函数如果你C#代码没访问到的就不编译到你最终发布包
解决办法:增加引用(比如配置到LuaCallCSharp,或者你自己C#代码增加那函数的访问),或者通过link.xml配置(当配置了ReflectionUse后,xlua会自动帮你配置到link.xml)告诉il2cpp别剪裁某类型。
## Unity 2018及以上版本兼容性问题解决
2.1.14前的版本都建议先升级到2.1.14,升级后,还有如下两个使用注意事项:
1、默认配置不生成代码运行会报错
这是因为Api Compatibility Level设置为.NET Standard 2.0,而.NET Standard 2.0不支持emit导致的。
解决方案:平时开发Api Compatibility Level设置为.NET 4.x,就能支持编辑器不生成代码开发。发布手机版本时,按Unity官方的建议,可配置为.NET Standard 2.0,包会更小些。
2、生成代码后,一些系统类型的生成代码会报一些方法不存在。
据研究表明,Unity 2018设置.NET 4.X Equivalent的话,其运行和编译用的库不一致,前者比后者多一些API。
运行用的是:unity安装目录\Editor\Data\MonoBleedingEdge\lib\mono\unityjit\mscorlib.dll
编译链接的是:unity安装目录\Editor\Data\MonoBleedingEdge\lib\mono\4.7.1-api\mscorlib.dll
解决办法:用黑名单排除报错方法即可。不过2019年8月6号以前的版本的黑名单配置对泛型不友好,要一个个泛型实例的配置(比如,Dictionary<int, int>和Dictionary<float, int>要分别配置),而目前发现该问题主要出在泛型Dictionary上。可以更新到2019年8月6号之后的版本,该版本支持配置一个过滤器对泛型方法过滤。这里有对unity 2018的Dictionary的[针对性配置](https://github.com/Tencent/xLua/blob/master/Assets/XLua/Editor/ExampleConfig.cs#L277),直接拷贝使用,如果碰到其它泛型也有多出来的方法,参考Dictionary进行配置。
## Plugins源码在哪里可以找到,怎么使用?
Plugins源码位于xLua_Project_Root/build下。
源码编译依赖cmake,安装cmake后执行make_xxxx_yyyy.zz即可,xxxx代表平台,比如iosandroid等,yyyy是要集成的虚拟机,有lua53和luajit两者,zz是后缀,windows下是bat,其它平台是sh。
windows编译依赖Visual Studio 2015。
android编译在linux下执行,依赖NDK,并且需要把脚本中ANDROID_NDK指向NDK的安装目录。
ios和osx需要在mac下编译。
## 报类似“xlua.access, no field __Hitfix0_Update”的错误怎么解决?
按[Hotfix操作指南](hotfix.md)一步步操作,以及注意事项。确保上述步骤完成后,可尝试使用[解决方案](https://github.com/Tencent/xLua/issues/850)。
出现这报错,肯定是这个导致的:最终包的这个方法(函数)没注入。
但造成“最终包的这个方法(函数)没注入”的原因会有很多:比如没按文档操作,注入失败,比如Hotfix列表漏了这个类,比如你的打包脚本在注入后,又触发了重新编译,覆盖了注入结果。。。
统一的解决方式是找出并解决导致“最终包的这个方法(函数)没注入”的具体原因。
## visual studio 2017下编译UWP原生库
visual studio 2017需要安装:1、“工作负载”下的“通用Window平台开发”;2、“单个组件”下的“用于ARM的Visual C++编译器和库”、“用于ARM64的Visual C++编译器和库”、“是用于ARM64的C++通用Windows平台工具”
## visual studio 2015下编译原生库
把build\vs2015下的bat文件拷贝到build目录,覆盖同名文件
## 报“please install the Tools”
没有把Tools安装到Assets平级目录,安装包,或者master下都能找到这个目录。
## 报“This delegate/interface must add to CSharpCallLua : XXX”异常怎么解决?
在编辑器下xLua不生成代码都可以运行,出现这种提示,要么是该类型没加CSharpCallLua,要么是加之前生成过代码,没重新执行生成。
解决办法,确认XXX(类型名)加上CSharpCallLua后,清除代码后运行。
如果编辑器下没问题,发布到手机报这错,表示你发布前没生成代码(执行“XLua/Generate Code”)。
如果你Unity版本大于或等于2018,看下前面兼容性的章节。
## unity5.5以上执行"XLua/Hotfix Inject In Editor"菜单会提示"WARNING: The runtime version supported by this application is unavailable."
这是因为注入工具是用.net3.5编译,而unity5.5意思MonoBleedingEdge的mono环境并没3.5支持导致的,不过一般而言都向下兼容,目前为止也没发现该warning带来什么问题。
可能有人发现定义INJECT_WITHOUT_TOOL用内嵌模式会没有该warning,但问题是这模式是调试问题用的,不建议使用,因为可能会有一些库冲突问题。
## hotfix下怎么触发一个event
首先通过xlua.private_accessible开启私有成员访问。
跟着通过对象的"&事件名"字段调用delegate,例如self\['&MyEvent'\](),其中MyEvent是事件名。
## 怎么对Unity Coroutine的实现函数打补丁?
见[Hotfix操作指南](hotfix.md)相应章节。
## 支持NGUI(或者UGUI/DOTween等等)么?
支持,xLua最主要的特性是让你原来用C#写的地方可以换成用lua写,你C#能用的插件,基本都能用。
## 如果需要调试,CustomLoader的filepath参数该如何处理?
lua里头调用require 'a.b'时,CustomLoader会被调用,并传入字符串"a.b",你需要理解这字符串,(从文件/内存/网络等)加载好lua文件,返回两个东西,第一个是调试器可以理解的路径,比如:a/b.lua,这个通过设置ref类型的filepath参数返回,第二个是UTF8格式的源码的字节流(byte[]),通过返回值返回。
## 什么是生成代码?
xLua支持的lua和C#间交互技术之一,这种技术通过生成两者间的适配代码来实现交互,性能较好,是推荐的方式。
另一种交互技术是反射,这种方式对安装包的影响更少,可以在性能要求不高或者对安装包大小很敏感的场景下使用。
## 改了接口后,之前生成的代码出现错误怎么办?
清除掉生成代码(执行“Clear Generated Code”菜单,如果你重启过,会找不到这个菜单,这时你可以手动删除整个生成代码目录),等编译完成后重新生成。
## 应该什么时候生成代码?
开发期不建议生成代码,可以避免很多由于不一致导致的编译失败,以及生成代码本身的编译等待。
build手机版本前必须执行生成代码,建议做成自动化的。
做性能调优,性能测试前必须执行生成代码,因为生成和不生成性能的区别还是很大的。
## CS名字空间下有所有C# API是不是很占内存?
由于用了lazyload,这个“有”只是个虚拟的概念,比如UnityEngine.GameObject,是访问第一次CS.UnityEngine.GameObject或者第一个实例往lua传送才加载该类型方法,属性等。
## LuaCallSharp以及CSharpCallLua两种生成各在什么场景下用?
看调用者和被调用者,比如要在lua调用C#的GameObject.Find函数,或者调用gameobject的实例方法,属性等,GameObject类要加LuaCallSharp,而想把一个lua函数挂到UI回调,这是调用者是C#,被调用的是一个lua函数,所以回调声明的delegate要加CSharpCallLua。
有时会比较迷惑人,比如List<int>.Find(Predicate<int> match)的调用,List<int>当然是加LuaCallSharp,而Predicate<int>却要加CSharpCallLua,因为match的调用者在C#,被调用的是一个lua函数。
更无脑一点的方式是看到“This delegate/interface must add to CSharpCallLua : XXX”,就把XXX加到CSharpCallLua即可。
## 值类型传递会有gc alloc么?
如果你使用的是delegate调用lua函数,或者用LuaTable、LuaFunction的无gc接口,或者数组的话,以下值类型都是没gc的:
1、所有的基本值类型(所有整数,所有浮点数,decimal);
2、所有的枚举类型;
3、字段只包含值类型的struct,可嵌套其它只包含值类型struct;
其中2、3需要把该类型加到GCOptimize。
## 反射在ios下可用吗?
ios下的限制有两个:1、没有jit;2、代码剪裁(stripping);
对于C#通过delegate或者interface调用lua,如果不生成代码是用反射的emit,这依赖jit,所以这目前只在编辑器可用。
对于lua调用C#,主要会被代码剪裁影响,这时你可以配置ReflectionUse(不要配LuaCallSharp),执行“Generate Code”,这时不会对该类生成封装代码,而是生成link.xml把该类配置为不剪裁。
简而言之,除了CSharpCallLua是必须的(这类生成代码往往不多),LuaCallSharp生成都可以改为用反射。
## 支持泛型方法的调用么?
1、泛型约束到某个基类的支持,看例子:(../Examples/09_GenericMethod/)
2、没有泛型约束的建议封装为非泛型使用
如果是静态方法,可以自己写个封装来实例化泛型方法。
如果是成员方法,xLua支持扩展方法,你可以添加一个扩展方法来实例化泛型方法。该扩展方法使用起来就和普通成员方法一样。
```csharp
// C#
public static Button GetButton(this GameObject go)
{
return go.GetComponent<Button>();
}
```
```lua
-- lua
local go = CS.UnityEngine.GameObject.Find("button")
go:GetButton().onClick:AddListener(function()
print('onClick')
end)
```
3、如果xlua版本大于2.1.12的话,新增反射调用泛型方法的支持(有一定的限制,看后面的说明),比如对于这么个C#类型
```csharp
public class GetGenericMethodTest
{
int a = 100;
public int Foo<T1, T2>(T1 p1, T2 p2)
{
Debug.Log(typeof(T1));
Debug.Log(typeof(T2));
Debug.Log(p1);
Debug.Log(p2);
return a;
}
public static void Bar<T1, T2>(T1 p1, T2 p2)
{
Debug.Log(typeof(T1));
Debug.Log(typeof(T2));
Debug.Log(p1);
Debug.Log(p2);
}
}
```
在lua那这么调用:
```lua
local foo_generic = xlua.get_generic_method(CS.GetGenericMethodTest, 'Foo')
local bar_generic = xlua.get_generic_method(CS.GetGenericMethodTest, 'Bar')
local foo = foo_generic(CS.System.Int32, CS.System.Double)
local bar = bar_generic(CS.System.Double, CS.UnityEngine.GameObject)
-- call instance method
local o = CS.GetGenericMethodTest()
local ret = foo(o, 1, 2)
print(ret)
-- call static method
bar(2, nil)
```
使用限制,只有下面几种情况可以:
* mono下可以
* il2cpp下,如果泛型参数是引用类型可以
* il2cpp下,如果泛型参数是值类型,C#那有用同样的泛型参数调用过(如果是hotfix场景下,一般在C#那会用同样的泛型参数调用过,所以在hotfix功能下一般都可用)
## 支持lua调用C#重载函数吗
支持,但没有C#端支持的那么完善,比如重载方法void Foo(int a)和void Foo(short a),由于int和short都对应lua的number,是没法根据参数判断调用的是哪个重载。这时你可以借助扩展方法来为其中一个起一个别名。
## 编辑器下运行正常,打包的时候生成代码报“没有某方法/属性/字段定义”怎么办?
往往是由于该方法/属性/字段是扩在条件编译里头,只在UNITY_EDITOR下有效,这是可以通过把这方法/属性/字段加到黑名单来解决,加了之后要等编译完成后重新执行代码生成。
## this[string field]或者this[object field]操作符重载为什么在lua无法访问?(比如Dictionary\<string, xxx\>, Dictionary\<object, xxx\>在lua中无法通过dic['abc']或者dic.abc检索值)
因为:1、这个特性会导致基类定义的方法、属性、字段等无法访问(比如Animation无法访问到GetComponent方法);2、key为当前类某方法、属性、字段的名字的数据无法检索,比如Dictionary类型,dic['TryGetValue']返回的是一个函数,指向Dictionary的TryGetValue方法。
如果你的版本大于2.1.11,可以用get_Item来获取值,用set_Item来设置值。要注意只有this[string field]或者this[object field]才有这两个替代api,其它类型的key是没有的。
~~~lua
dic:set_Item('a', 1)
dic:set_Item('b', 2)
print(dic:get_Item('a'))
print(dic:get_Item('b'))
~~~
如果你的版本小于或等于2.1.11,建议直接方法该操作符的等效方法,比如Dictionary的TryGetValue,如果该方法没有提供,可以在C#那通过Extension method封装一个使用。
## 有的Unity对象,在C#为null,在lua为啥不为nil呢?比如一个已经Destroy的GameObject
其实那C#对象并不为null,是UnityEngine.Object重载的==操作符,当一个对象被Destroy,未初始化等情况,obj == null返回true,但这C#对象并不为null,可以通过System.Object.ReferenceEquals(null, obj)来验证下。
对应这种情况,可以为UnityEngine.Object写一个扩展方法:
~~~csharp
[LuaCallCSharp]
[ReflectionUse]
public static class UnityEngineObjectExtention
{
public static bool IsNull(this UnityEngine.Object o) // 或者名字叫IsDestroyed等等
{
return o == null;
}
}
~~~
然后在lua那你对所有UnityEngine.Object实例都使用IsNull判断
~~~lua
print(go:GetComponent('Animator'):IsNull())
~~~
## 泛型实例怎么构造
涉及的类型都在mscorlibAssembly-CSharp程序集的话,泛型实例的构造和普通类型是一样的,都是CS.namespace.typename(),可能比较特殊的是typename的表达,泛型实例的typename的表达包含了标识符非法符号,最后一部分要换成["typename"],以List<string>为例
~~~lua
local lst = CS.System.Collections.Generic["List`1[System.String]"]()
~~~
如果某个泛型实例的typename不确定,可以在C#测打印下typeof(不确定的类型).ToString()
如果涉及mscorlibAssembly-CSharp程序集之外的类型的话,可以用C#的反射来做
~~~lua
local dic = CS.System.Activator.CreateInstance(CS.System.Type.GetType('System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[UnityEngine.Vector3, UnityEngine]],mscorlib'))
dic:Add('a', CS.UnityEngine.Vector3(1, 2, 3))
print(dic:TryGetValue('a'))
~~~
如果你的xLua版本大于v2.1.12,将会有更漂亮的表达方式
~~~lua
-- local List_String = CS.System.Collections.Generic['List<>'](CS.System.String) -- another way
local List_String = CS.System.Collections.Generic.List(CS.System.String)
local lst = List_String()
local Dictionary_String_Vector3 = CS.System.Collections.Generic.Dictionary(CS.System.String, CS.UnityEngine.Vector3)
local dic = Dictionary_String_Vector3()
dic:Add('a', CS.UnityEngine.Vector3(1, 2, 3))
print(dic:TryGetValue('a'))
~~~
## 调用LuaEnv.Dispose时,报“try to dispose a LuaEnv with C# callback!”错是什么原因?
这是由于C#还存在指向lua虚拟机里头某个函数的delegate,为了防止业务在虚拟机释放后调用这些无效(因为其引用的lua函数所在虚拟机都释放了)delegate导致的异常甚至崩溃,做了这个检查。
怎么解决?释放这些delegate即可,所谓释放,在C#中,就是没有引用:
你是在C#通过LuaTable.Get获取并保存到对象成员,赋值该成员为null;
你是在lua那把lua函数注册到一些事件事件回调,反注册这些回调;
如果你是通过xlua.hotfix(class, method, func)注入到C#,则通过xlua.hotfix(class, method, nil)删除;
要注意以上操作在Dispose之前完成。
xlua提供了一个工具函数来帮助你找到被C#引用着的lua函数util.print_func_ref_by_csharp,使用很简单,执行如下lua代码:
~~~lua
local util = require 'xlua.util'
util.print_func_ref_by_csharp()
~~~
可以看到控制台有类似这样的输出,下面第一行表示有一个在main.lua的第2行定义的函数被C#引用着
~~~bash
LUA: main.lua:2
LUA: main.lua:13
~~~
## 调用LuaEnv.Dispose崩溃
很可能是这个Dispose操作是由lua那驱动执行,相当于在lua执行的过程中把lua虚拟机给释放了,改为只由C#执行即可
## C#参数(或字段)类型是object时,传递整数默认是以long类型传递,如何指明其它类型?比如int
看[例子11](../Examples/11_RawObject/RawObjectTest.cs)
## 如何做到先执行原来的C#逻辑,然后再执行补丁
用util.hotfix_ex,可以调用原先的C#逻辑
~~~lua
local util = require 'xlua.util'
util.hotfix_ex(CS.HotfixTest, 'Add', function(self, a, b)
local org_sum = self:Add(a, b)
print('org_sum', org_sum)
return a + b
end)
~~~
## 怎么把C#的函数赋值给一个委托字段
2.1.8及之前版本,你把C#函数当成一个lua函数即可,性能会略低,因为委托调用时先通过Birdage适配代码调用lua,然后lua再调用回C#。
2.1.9 xlua.util新增createdelegate函数
比如如下C#代码
~~~csharp
public class TestClass
{
public void Foo(int a)
{
}
public static void SFoo(int a)
{
}
public delegate void TestDelegate(int a);
~~~
你可以指明用Foo函数创建一个TestDelegate实例
~~~lua
local util = require 'xlua.util'
local d1 = util.createdelegate(CS.TestDelegate, obj, CS.TestClass, 'Foo', {typeof(CS.System.Int32)}) --由于Foo是实例方法,所以参数2需要传TestClass实例
local d2 = util.createdelegate(CS.TestDelegate, nil, CS.TestClass, 'SFoo', {typeof(CS.System.Int32)})
obj_has_TestDelegate.field = d1 + d2 --到时调用field的时候将会触发Foo和SFoo,这不会经过Lua适配
~~~
## 为什么有时Lua错误直接中断了而没错误信息?
一般两种情况:
1、你的错误代码用协程跑,而标准的lua,协程出错是通过resume返回值来表示,可以查阅相关的lua官方文档。如果你希望协程出错直接抛异常,可以在你的resume调用那加个assert。
把类似下面的代码:
~~~lua
coroutine.resume(co, ...)
~~~
改为:
~~~lua
assert(coroutine.resume(co, ...))
~~~
2、上层catch后,不打印
比如某些sdk,在回调业务时,try-catch后把异常吃了。
## 重载含糊如何处理
比如由于忽略out参数导致的Physics.Raycast其中一个重载调用不了,比如short,int无法区分的问题。
首先out参数导致重载含糊比较少见,目前只反馈(截至2017-9-22)过Physics.Raycast一个,建议通过自行封装来解决(short,int这种情况也适用):静态函数的直接封装个另外名字的,如果是成员方法则通过Extension method来封装。
如果是hotfix场景,我们之前并没有提前封装,又希望调用指定重载怎么办?
可以通过xlua.tofunction结合反射来处理,xlua.tofunction输入一个MethodBase对象,返回一个lua函数。比如下面的C#代码
~~~csharp
class TestOverload
{
public int Add(int a, int b)
{
Debug.Log("int version");
return a + b;
}
public short Add(short a, short b)
{
Debug.Log("short version");
return (short)(a + b);
}
}
~~~
我们可以这么调用指定重载:
~~~lua
local m1 = typeof(CS.TestOverload):GetMethod('Add', {typeof(CS.System.Int16), typeof(CS.System.Int16)})
local m2 = typeof(CS.TestOverload):GetMethod('Add', {typeof(CS.System.Int32), typeof(CS.System.Int32)})
local f1 = xlua.tofunction(m1) --切记对于同一个MethodBase,只tofunction一次,然后重复使用
local f2 = xlua.tofunction(m2)
local obj = CS.TestOverload()
f1(obj, 1, 2) --调用short版本,成员方法,所以要传对象,静态方法则不需要
f2(obj, 1, 2) --调用int版本
~~~
注意:xlua.tofunction由于使用不太方便,以及使用了反射,所以建议做作为临时方案,尽量用封装的方法来解决。
## 支持interface扩展方法么?
考虑到生成代码量,不支持通过obj:ExtentionMethod()的方式去调用,支持通过静态方法的方式去调用CS.ExtentionClass.ExtentionMethod(obj)
## 如何把xLua的Wrap生成操作集成到我项目的自动打包流程中?
可以参考[例子13](../Examples/13_BuildFromCLI/),通过命令行调用Unity自定义类方法出包。
## 使用热补丁特性,打手机版本时报DelegatesGensBridge.cs引用了不存在的类(比如仅编辑器使用的类型),应该如何处理?
这是因为Hotfix列表里头配置的类型(假设是类型A),对这些不存在的类型(假设是类型B)引用。找到这种类型,从Hotfix配置列表中排除(注意,排除的类型A,而不是类型B)。
如何找?VS中选中报不存在的类型,然后“Find All References”找到引用了这个类型的所有方法、属性。。这些方法、属性等等所在的类型就是你要排除的类型。
## 如何加载字节码
用luac编译后直接加载即可。
要注意默认lua字节码是区分32位和64位的,32位luac生成的字节码只能在32位虚拟机里头跑,可以按《[通用字节码](compatible_bytecode.md)》一文处理下。
## lua持有的c#对象怎么释放
达成下面两点即可释放:
* 1、lua所有对该C#对象释放
* 2、lua完成一次gc周期
貌似所有gc都是上述条件,但对于lua要特别说明下第二点,lua不像C#那样会后台启动一个gc线程在做垃圾回收,而是把gc拆分成小步骤插入到有内存分配。
所以注意一点:你没在运行lua代码,或者lua代码运行并未分配内存,你怎么等也不会有内存回收。
默认gc配置,lua要到达上次内存回收完成时内存占用的两倍才开启一轮新的gc周期,举例上次回收完毕20M内存,那么下次要等到40M才开始一轮gc周期。
按xLua的设计,一个C#对象引用传递到lua,仅让lua增加4字节内存(然而这可能是在C#侧内存占用很大的对象,比如贴图),可以看到通过持有C#引用要达成默认配置开启gc周期条件是比较困难的
这时可以这么干:
* 1、设置GcPause,让gc更快开启,默认200表示2倍上次回收内存时开启,coco2dx设置为100,表示完成一趟gc后马上开启下一趟,另外也可以设置GcStepmul来加快gc回收速度,默认是200表示回收比内存分配快两倍,GcStepmul在coco2dx设置为5000
* 2、可以在场景切换之类对于性能要求不高的地方加入全量gc调用(通过LuaEnv.FullGc或者在lua里头调用collectgarbage('collect')都可以)。
## 多线程下莫名crash怎么解决
多线程使用需要在(Player Setting/Scripting Define Symbols)下添加THREAD_SAFE宏。
常见的不明显的多线程的场景,比如c#异步socket,对象析构函数等。
## maOS10.15以上,启动unity的时候提示xlua.bundle损坏,移动到废纸篓
执行
~~~bash
sudo xattr -r -d com.apple.quarantine xlua.bundle
~~~
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 13645b7c8597d7840adaff65764ad40f
timeCreated: 1481765808
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,139 @@
# 特性
## 总体
* Lua虚拟机支持
* Lua5.3
* Luajit2.1
* Unity3D版本支持
* 各版本均支持
* 平台支持
* windows 64/32
* android
* ios 64/32/bitcode
* osx
* uwp
* webgl
* 互访技术
* 生成适配代码
* 反射
* 易用性
* 解压即可用
* 开发期无需生成代码
* 生成代码和反射间可无缝切换
* 更简单的无GC api
* 菜单简单易懂
* 配置可以多份,按模块划分,也可以直接在目标类型上打Attribute标签
* 自动生成link.xml防止代码剪裁
* Plugins部分采用cmake编译,更简单
* 核心代码不依赖生成代码,可以随时删除生成目录
* 性能
* Lazyload技术,避免用不上的类型的开销
* lua函数映射到c# delegatelua table映射到interface,可实现接口层面无C# gc alloc开销
* 所有基本值类型,所有枚举,字段都是值类型的struct,在Lua和C#间传递无C# gc alloc
* LuaTableLuaFunction提供无gc访问接口
* 通过代码生成期的静态分析,生成最优代码
* 支持C#和Lua间指针传递
* 自动解除已经Destroy的UnityEngine.Object的引用
* 扩展性
* 不用改代码就可以加入Lua第三方扩展
* 生成引擎提供接口做二次开发
## 支持为如下C#实现打补丁
* 构造函数
* 析构函数
* 成员函数
* 静态函数
* 泛化函数
* 操作符重载
* 成员属性
* 静态属性
* 事件
## Lua代码加载
* 加载字符串
* 支持加载后立即执行
* 支持加载后返回一个delegate或者LuaFunction,调用delegate或者LuaFunction后可传脚本参数
* Resources目录的文件
* 直接require
* 自定义loader
* Lua里头require时触发
* require参数透传给loaderloader读取Lua代码返回
* Lua原有的方式
* Lua原有的方式都保留
## Lua调用C#
* 创建C#对象
* C#静态属性,字段
* C#静态方法
* C#成员属性,字段
* C#成员方法
* C#继承
* 子类对象可以直接调用父类的方法,访问父类属性
* 子类模块可以直接调用父类的静态方法,静态属性
* 扩展方法(Extension methods
* 就像普通成员方法一样使用
* 参数的输入输出属性(out,ref)
* out对应一个lua返回值
* ref对应一个lua参数以及一个lua返回值
* 函数重载
* 支持重载
* 由于lua数据类型远比C#要少,会出现无法判断的情况,可通过扩展方法来来调用。
* 操作符重载
* 支持的操作符:+-*/==,一元-<<= %[]
* 其它操作符可以借助扩展方法调用
* 参数默认值
* C#参数有默认值,在lua可以不传
* 可变参数
* 在对应可变参数部分,直接输入一个个参数即可,不需要把这些参数扩到一个数组里头
* 泛化方法调用
* 静态方法可以自行封装使用
* 成员函数可通过扩展方法封装使用
* 枚举类型
* 数字或字符串到枚举的转换
* delegate
* 调用一个C# delegate
* +操作符
* -操作符
* 把一个lua函数作为一个c# delegate传递给c#
* event
* 增加事件回调
* 移除事件回调
* 64位整数
* 传递无gc而且无精度损失
* lua53下使用原生64位支持
* 可以和number运算
* 以java的方式支持无符号64位整数
* table的自动转换到C#复杂类型
* obj.complexField = {a = 1, b = {c = 1}}obj是一个C#对象complexField是两层嵌套的struct或者class
* typeof
* 对应C#的typeof操作符,返回Type对象
* lua侧直接clone
* decimal
* 传递无gc而且无精度损失
## C#调用Lua
* 调用Lua函数
* 以delegate方式调用Lua函数
* 以LuaFunction调用lua函数
* 访问Lua的table
* LuaTable的泛化Get/Set接口,调用无gc,可指明KeyValue的类型
* 用标注了CSharpCallLua的interface访问
* 值拷贝到structclass
## Lua虚拟机
* 虚拟机gc参数读取及设置
## 工具链
* Lua Profiler
* 可根据函数调用总时长,平均每次调用时长,调用次数排序
* 显示lua函数名及其所在文件的名字及行号
* 如果C#函数,会显示这个是C#函数
* 支持真机调试
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6dcae34981e53564ea1ad644fe0b2f7c
timeCreated: 1481768591
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
+372
View File
@@ -0,0 +1,372 @@
## 使用方式
1、打开该特性
添加HOTFIX_ENABLE宏,(在Unity3D的File->Build Setting->Scripting Define Symbols下添加)。编辑器、各手机平台这个宏要分别设置!如果是自动化打包,要注意在代码里头用API设置的宏是不生效的,需要在编辑器设置。
(建议平时开发业务代码不打开HOTFIX_ENABLE,只在build手机版本或者要在编译器下开发补丁时打开HOTFIX_ENABLE
2、执行XLua/Generate Code菜单。
3、注入,构建手机包这个步骤会在构建时自动进行,编辑器下开发补丁需要手动执行"XLua/Hotfix Inject In Editor"菜单。打印“hotfix inject finish!”或者“had injected!”才算成功,否则会打印错误信息。
如果已经打印了“hotfix inject finish!”或者“had injected!”,执行xlua.hotfix仍然报类似“xlua.access, no field __Hitfix0_Update”的错误,要么是该类没配置到Hotfix列表,要么是注入成功后,又触发了编译,覆盖了注入结果。
## 约束
不支持静态构造函数。
目前只支持Assets下代码的热补丁,不支持引擎,c#系统库的热补丁
## API
xlua.hotfix(class, [method_name], fix)
* 描述 : 注入lua补丁
* class C#类,两种表示方法,CS.Namespace.TypeName或者字符串方式"Namespace.TypeName",字符串格式和C#的Type.GetType要求一致,如果是内嵌类型(Nested Type)是非Public类型的话,只能用字符串方式表示"Namespace.TypeName+NestedTypeName"
* method_name 方法名,可选;
* fix 如果传了method_namefix将会是一个function,否则通过table提供一组函数。table的组织按key是method_namevalue是function的方式。
base(csobj)
* 描述 子类override函数通过base调用父类实现。
* csobj 对象
* 返回值 : 新对象,可以通过该对象base上的方法
例子(位于HotfixTest2.cs):
```lua
xlua.hotfix(CS.BaseTest, 'Foo', function(self, p)
print('BaseTest', p)
base(self):Foo(p)
end)
```
util.hotfix_ex(class, method_name, fix)
* 描述 xlua.hotfix的增强版本,可以在fix函数里头执行原来的函数,缺点是fix的执行会略慢。
* method_name 方法名;
* fix : 用来替换C#方法的lua function。
## 标识要热更新的类型
和其它配置一样,有两种方式
方式一:直接在类里头打Hotfix标签(不建议,示例只是为了方便演示采取这种方式);
!!注意,方式一在高版本unity不支持
方式二:在一个static类的static字段或者属性里头配置一个列表。属性可以用于实现的比较复杂的配置,比如根据Namespace做白名单。
!!注意,高版本Unity需要把配置文件放Editor目录下
~~~csharp
//如果涉及到Assembly-CSharp.dll之外的其它dll,如下代码需要放到Editor目录
public static class HotfixCfg
{
[Hotfix]
public static List<Type> by_field = new List<Type>()
{
typeof(HotFixSubClass),
typeof(GenericClass<>),
};
[Hotfix]
public static List<Type> by_property
{
get
{
return (from type in Assembly.Load("Assembly-CSharp").GetTypes()
where type.Namespace == "XXXX"
select type).ToList();
}
}
}
~~~
## Hotfix Flag
Hotfix标签可以设置一些标志位对生成代码及插桩定制化
* Stateless、Stateful
遗留设置,Stateful方式在新版本已经删除,因为这种方式可以用xlua.util.state接口达到类似的效果,该接口的使用可以看下HotfixTest2.cs里的示例代码。
由于没Stateful,默认就是Stateless,所以也没必要设置该标志位。
* ValueTypeBoxing
值类型的适配delegate会收敛到object,好处是代码量更少,不好的是值类型会产生boxing及gc,适用于对text段敏感的业务。
* IgnoreProperty
不对属性注入及生成适配代码,一般而言,大多数属性的实现都很简单,出错几率比较小,建议不注入。
* IgnoreNotPublic
不对非public的方法注入及生成适配代码。除了像MonoBehaviour那种会被反射调用的私有方法必须得注入,其它仅被本类调用的非public方法可以不注入,只不过修复时会工作量稍大,所有引用到这个函数的public方法都要重写。
* Inline
不生成适配delegate,直接在函数体注入处理代码。
* IntKey
不生成静态字段,而是把所有注入点放到一个数组集中管理。
好处:对text段影响小。
坏处:使用不像默认方式那么方便,需要通过id来指明hotfix哪个函数,而这个id是代码注入工具时分配的,函数到id的映射会保存在Gen/Resources/hotfix_id_map.lua.txt,并且自动加时间戳备份到hotfix_id_map.lua.txt同级目录,发布手机版本后请妥善保存该文件。
该文件的格式大概如下(注意:该文件仅IntKey模式使用,当你没类型指定IntKey模式注入,该文件只返回个空表):
~~~lua
return {
["HotfixTest"] = {
[".ctor"] = {
5
},
["Start"] = {
6
},
["Update"] = {
7
},
["FixedUpdate"] = {
8
},
["Add"] = {
9,10
},
["OnGUI"] = {
11
},
},
}
~~~
想要替换HotfixTest的Update函数,你得
~~~lua
CS.XLua.HotfixDelegateBridge.Set(7, func)
~~~
如果是重载函数,将会一个函数名对应多个id,比如上面的Add函数。
能不能自动化一些呢?可以,xlua.util提供了auto_id_map函数,执行一次后你就可以像以前那样直接用类,方法名去指明修补的函数。
~~~lua
(require 'xlua.util').auto_id_map()
xlua.hotfix(CS.HotfixTest, 'Update', function(self)
self.tick = self.tick + 1
if (self.tick % 50) == 0 then
print('<<<<<<<<Update in lua, tick = ' .. self.tick)
end
end)
~~~
前提是hotfix_id_map.lua.txt放到可以通过require 'hotfix_id_map'引用到的地方。
## 使用建议
* 对所有较大可能变动的类型加上Hotfix标识;
* 建议用反射找出所有函数参数、字段、属性、事件涉及的delegate类型,标注CSharpCallLua
* 业务代码、引擎API、系统API,需要在Lua补丁里头高性能访问的类型,加上LuaCallCSharp
* 引擎API、系统API可能被代码剪裁调(C#无引用的地方都会被剪裁),如果觉得可能会新增C#代码之外的API调用,这些API所在的类型要么加LuaCallCSharp,要么加ReflectionUse
## 打补丁
xlua可以用lua函数替换C#的构造函数,函数,属性,事件的替换。lua实现都是函数,比如属性对于一个getter函数和一个setter函数,事件对应一个add函数和一个remove函数。
* 函数
method_name传函数名,支持重载,不同重载都是转发到同一个lua函数。
比如:
```csharp
// 要fix的C#类
[Hotfix]
public class HotfixCalc
{
public int Add(int a, int b)
{
return a - b;
}
public Vector3 Add(Vector3 a, Vector3 b)
{
return a - b;
}
```
```lua
xlua.hotfix(CS.HotfixCalc, 'Add', function(self, a, b)
return a + b
end)
```
静态函数和成员函数的区别是,成员函数会加一个self参数,这个self在Stateless方式下是C#对象本身(对应C#的this
普通参数对于lua的参数,ref参数对应lua的一个参数和一个返回值,out参数对于lua的一个返回值。
泛化函数的打补丁规则和普通函数一样。
* 构造函数
构造函数对应的method_name是".ctor"。
和普通函数不一样的是,构造函数的热补丁并不是替换,而是执行原有逻辑后调用lua。
* 属性
对于名为“AProp”的属性,会对应一个gettermethod_name等于get_APropsetter的method_name等于set_AProp。
* []操作符
赋值对应set_Item,取值对应get_Item。第一个参数是self,赋值后面跟key,value,取值只有key参数,返回值是取出的值。
* 其它操作符
C#的操作符都有一套内部表示,比如+号的操作符函数名是op_Addition(其它操作符的内部表示可以去请参照相关资料),覆盖这函数就覆盖了C#的+号操作符。
* 事件
比如对于事件“AEvent”,+=操作符是add_AEvent-=对应的是remove_AEvent。这两个函数均是第一个参数是self,第二个参数是操作符后面跟的delegate。
通过xlua.private_accessible(版本号大于2.1.11不需要调用xlua.private_accessible)来直接访问事件对应的私有delegate的直接访问后,可以通过对象的"&事件名"字段直接触发事件,例如self\['&MyEvent'\](),其中MyEvent是事件名。
* 析构函数
method_name是"Finalize",传一个self参数。
和普通函数不一样的是,析构函数的热补丁并不是替换,而是开头调用lua函数后继续原有逻辑。
* 泛化类型
其它规则一致,需要说明的是,每个泛化类型实例化后都是一个独立的类型,只能针对实例化后的类型分别打补丁。比如:
```csharp
public class GenericClass<T>
{
```
你只能对GenericClass\<double\>GenericClass\<int\>这些类,而不是对GenericClass打补丁。
对GenericClass<double>打补丁的实例如下:
```csharp
luaenv.DoString(@"
xlua.hotfix(CS.GenericClass(CS.System.Double), {
['.ctor'] = function(obj, a)
print('GenericClass<double>', obj, a)
end;
Func1 = function(obj)
print('GenericClass<double>.Func1', obj)
end;
Func2 = function(obj)
print('GenericClass<double>.Func2', obj)
return 1314
end
})
");
```
* Unity协程
通过util.cs_generator可以用一个function模拟一个IEnumerator,在里头用coroutine.yield,就类似C#里头的yield return。比如下面的C#代码和对应的hotfix代码是等同效果的
~~~csharp
[XLua.Hotfix]
public class HotFixSubClass : MonoBehaviour {
IEnumerator Start()
{
while (true)
{
yield return new WaitForSeconds(3);
Debug.Log("Wait for 3 seconds");
}
}
}
~~~
~~~csharp
luaenv.DoString(@"
local util = require 'xlua.util'
xlua.hotfix(CS.HotFixSubClass,{
Start = function(self)
return util.cs_generator(function()
while true do
coroutine.yield(CS.UnityEngine.WaitForSeconds(3))
print('Wait for 3 seconds')
end
end)
end;
})
");
~~~
* 整个类
如果要替换整个类,不需要一次次的调用xlua.hotfix去替换,可以整个一次完成。只要给一个table,按method_name = function组织即可
```lua
xlua.hotfix(CS.StatefullTest, {
['.ctor'] = function(csobj)
return util.state(csobj, {evt = {}, start = 0, prop = 0})
end;
set_AProp = function(self, v)
print('set_AProp', v)
self.prop = v
end;
get_AProp = function(self)
return self.prop
end;
get_Item = function(self, k)
print('get_Item', k)
return 1024
end;
set_Item = function(self, k, v)
print('set_Item', k, v)
end;
add_AEvent = function(self, cb)
print('add_AEvent', cb)
table.insert(self.evt, cb)
end;
remove_AEvent = function(self, cb)
print('remove_AEvent', cb)
for i, v in ipairs(self.evt) do
if v == cb then
table.remove(self.evt, i)
break
end
end
end;
Start = function(self)
print('Start')
for _, cb in ipairs(self.evt) do
cb(self.start, 2)
end
self.start = self.start + 1
end;
StaticFunc = function(a, b, c)
print(a, b, c)
end;
GenericTest = function(self, a)
print(self, a)
end;
Finalize = function(self)
print('Finalize', self)
end
})
```
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a96cb06c040f28c4aab024ca7634360b
timeCreated: 1482837382
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

@@ -0,0 +1,56 @@
fileFormatVersion: 2
guid: 6b9f4e2e38c36db40bc5bdfe20038d94
timeCreated: 1481715979
licenseType: Pro
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 1
linearTexture: 0
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: .25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 8
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -1
maxTextureSize: 2048
textureSettings:
filterMode: -1
aniso: -1
mipBias: -1
wrapMode: -1
nPOTScale: 1
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: .5, y: .5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 0
textureType: -1
buildTargetSettings: []
spriteSheet:
sprites: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,12 @@
## 源代码签名的用处
可以防止文件传输的过程被黑客篡改。
## xLua的签名功能的使用
* 用Tools/KeyPairsGen.exe生成公私钥对,key_ras文件保存的是私钥,key_ras.pub保存的是公钥,这两个文件请妥善保存,私钥关系到游戏安全,请做好保密工作;
* 用Tools/FilesSignature.exe对源代码进行签名:
* key_ras文件要放到执行目录;
* 参数是源目录和目标目录,这个工具会自动把源目录及其子目录下所有lua后缀的文件签名,按同样的目录结构放到目标目录下;
* 通过SignatureLoader对自己原有的CustomLoader包装后使用;
* SignatureLoader的构造函数有两个,一个是公钥,也就是key_ras.pub里头的内容,一个是原来的Loader;
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 54ea702ea6cb89a46b7986dc7fa8482e
timeCreated: 1489376033
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Some files were not shown because too many files have changed in this diff Show More