mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-22 01:36:42 +08:00
Merge branch '4.2' into 4.3-beta
# Conflicts: # spine-unity/Assets/Spine/package.json
This commit is contained in:
commit
9b596c3856
1
.github/pull_request_template.md
vendored
Normal file
1
.github/pull_request_template.md
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
* [ ] I confirm this contribution is made under the Esoteric Software LLC [CLA](http://esotericsoftware.com/licenses/cla.txt).
|
||||||
@ -49,7 +49,7 @@ jobs:
|
|||||||
path: spine-godot/example-v4-extension/bin/windows/*.dll
|
path: spine-godot/example-v4-extension/bin/windows/*.dll
|
||||||
|
|
||||||
build-linux-x86_64:
|
build-linux-x86_64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
|
|||||||
4
.github/workflows/spine-godot-v4.yml
vendored
4
.github/workflows/spine-godot-v4.yml
vendored
@ -65,7 +65,7 @@ jobs:
|
|||||||
path: spine-godot/godot/bin/**/*
|
path: spine-godot/godot/bin/**/*
|
||||||
|
|
||||||
godot-editor-linux:
|
godot-editor-linux:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
@ -161,7 +161,7 @@ jobs:
|
|||||||
path: spine-godot/godot/bin/macos.zip
|
path: spine-godot/godot/bin/macos.zip
|
||||||
|
|
||||||
godot-template-linux:
|
godot-template-linux:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -221,3 +221,4 @@ spine-godot/build/version.txt
|
|||||||
spine-godot/vc140.pdb
|
spine-godot/vc140.pdb
|
||||||
spine-godot/example-v4-extension/bin
|
spine-godot/example-v4-extension/bin
|
||||||
spine-godot/example-v4-extension/MoltenVK.xcframework
|
spine-godot/example-v4-extension/MoltenVK.xcframework
|
||||||
|
spine-flutter/example/android/app/.cxx
|
||||||
|
|||||||
@ -175,6 +175,7 @@
|
|||||||
2. Add a `RenderExistingMeshGraphic` component.
|
2. Add a `RenderExistingMeshGraphic` component.
|
||||||
3. In the `RenderExistingMeshGraphic` component Inspector at `Reference Skeleton Graphic` assign the original `SkeletonGraphic` object.
|
3. In the `RenderExistingMeshGraphic` component Inspector at `Reference Skeleton Graphic` assign the original `SkeletonGraphic` object.
|
||||||
4. At `Replacement Material` assign e.g. the included _SkeletonGraphicDefaultOutline_ material to replace all materials with this material. Alternatively, if `Multiple CanvasRenderers` is enabled at the reference SkeletonGraphic, you can add entries to the `Replacement Materials` list and at each entry assign the original SkeletonGraphic material (e.g. _SkeletonGraphicDefault_) to be replaced and the respective `Replacement Material` (e.g. _SkeletonGraphicDefaultOutline_).
|
4. At `Replacement Material` assign e.g. the included _SkeletonGraphicDefaultOutline_ material to replace all materials with this material. Alternatively, if `Multiple CanvasRenderers` is enabled at the reference SkeletonGraphic, you can add entries to the `Replacement Materials` list and at each entry assign the original SkeletonGraphic material (e.g. _SkeletonGraphicDefault_) to be replaced and the respective `Replacement Material` (e.g. _SkeletonGraphicDefaultOutline_).
|
||||||
|
- Added option for unsafe direct data loading when loading skeleton binary data to avoid some allocations, enabled via build define `SPINE_ALLOW_UNSAFE`. This define can be set via Spine Preferences, setting `Unsafe Build Defines - Direct data access`. The define is disabled by default to maintain existing behaviour. Changed asmdef setting for spine-unity assembly to allow unsafe code, has no effect other than allowing setting the `SPINE_ALLOW_UNSAFE` define.
|
||||||
|
|
||||||
- **Breaking changes**
|
- **Breaking changes**
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
|
# 4.2.36
|
||||||
|
- Support for 16KB page alignement on Android. You must specify the NDK version in the build.gradle file of your app's Android project. See https://github.com/EsotericSoftware/spine-runtimes/issues/2849
|
||||||
|
|
||||||
# 4.2.35
|
# 4.2.35
|
||||||
- Port of commit f1e0f0f: Fixed animation not being mixed out in some cases.
|
- Port of commit f1e0f0f: Fixed animation not being mixed out in some cases.
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ buildscript {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// The Android Gradle Plugin knows how to build native code with the NDK.
|
// The Android Gradle Plugin knows how to build native code with the NDK.
|
||||||
classpath 'com.android.tools.build:gradle:7.1.2'
|
classpath 'com.android.tools.build:gradle:8.5.1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ android {
|
|||||||
|
|
||||||
// Bumping the plugin ndkVersion requires all clients of this plugin to bump
|
// Bumping the plugin ndkVersion requires all clients of this plugin to bump
|
||||||
// the version in their app and to download a newer version of the NDK.
|
// the version in their app and to download a newer version of the NDK.
|
||||||
ndkVersion "21.1.6352462"
|
ndkVersion "28.1.13356709"
|
||||||
|
|
||||||
// Invoke the shared CMake build with the Android Gradle Plugin.
|
// Invoke the shared CMake build with the Android Gradle Plugin.
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
@ -56,6 +56,6 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 21
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
spine-flutter/example/.gitignore
vendored
2
spine-flutter/example/.gitignore
vendored
@ -5,9 +5,11 @@
|
|||||||
*.swp
|
*.swp
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.atom/
|
.atom/
|
||||||
|
.build/
|
||||||
.buildlog/
|
.buildlog/
|
||||||
.history
|
.history
|
||||||
.svn/
|
.svn/
|
||||||
|
.swiftpm/
|
||||||
migrate_working_dir/
|
migrate_working_dir/
|
||||||
|
|
||||||
# IntelliJ related
|
# IntelliJ related
|
||||||
|
|||||||
@ -1,3 +1,9 @@
|
|||||||
|
plugins {
|
||||||
|
id "com.android.application"
|
||||||
|
id "kotlin-android"
|
||||||
|
id "dev.flutter.flutter-gradle-plugin"
|
||||||
|
}
|
||||||
|
|
||||||
def localProperties = new Properties()
|
def localProperties = new Properties()
|
||||||
def localPropertiesFile = rootProject.file('local.properties')
|
def localPropertiesFile = rootProject.file('local.properties')
|
||||||
if (localPropertiesFile.exists()) {
|
if (localPropertiesFile.exists()) {
|
||||||
@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
|
||||||
if (flutterRoot == null) {
|
|
||||||
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
|
||||||
}
|
|
||||||
|
|
||||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||||
if (flutterVersionCode == null) {
|
if (flutterVersionCode == null) {
|
||||||
flutterVersionCode = '1'
|
flutterVersionCode = '1'
|
||||||
@ -21,13 +22,11 @@ if (flutterVersionName == null) {
|
|||||||
flutterVersionName = '1.0'
|
flutterVersionName = '1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'com.android.application'
|
|
||||||
apply plugin: 'kotlin-android'
|
|
||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion flutter.compileSdkVersion
|
compileSdkVersion flutter.compileSdkVersion
|
||||||
ndkVersion flutter.ndkVersion
|
// ndkVersion flutter.ndkVersion
|
||||||
|
ndkVersion "28.1.13356709"
|
||||||
|
namespace "com.esotericsoftware.spine.android"
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
@ -44,7 +43,7 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
applicationId "com.example.spine_flutter_example"
|
applicationId "com.esotericsoftware.spine.android"
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
|
||||||
minSdkVersion flutter.minSdkVersion
|
minSdkVersion flutter.minSdkVersion
|
||||||
@ -65,7 +64,3 @@ android {
|
|||||||
flutter {
|
flutter {
|
||||||
source '../..'
|
source '../..'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
package="com.example.spine_flutter_example">
|
|
||||||
<!-- The INTERNET permission is required for development. Specifically,
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
the Flutter tool needs it to communicate with the running application
|
the Flutter tool needs it to communicate with the running application
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
package="com.example.spine_flutter_example">
|
|
||||||
<application
|
<application
|
||||||
android:label="spine_flutter_example"
|
android:label="spine_flutter_example"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package com.example.example
|
package com.esotericsoftware.spine.android
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
package com.example.spine_flutter_example
|
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
|
||||||
|
|
||||||
class MainActivity: FlutterActivity() {
|
|
||||||
}
|
|
||||||
@ -1,5 +1,4 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
package="com.example.spine_flutter_example">
|
|
||||||
<!-- The INTERNET permission is required for development. Specifically,
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
the Flutter tool needs it to communicate with the running application
|
the Flutter tool needs it to communicate with the running application
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
|||||||
@ -1,16 +1,3 @@
|
|||||||
buildscript {
|
|
||||||
ext.kotlin_version = '1.6.10'
|
|
||||||
repositories {
|
|
||||||
google()
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
classpath 'com.android.tools.build:gradle:7.1.2'
|
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
|
|||||||
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
|
||||||
|
|||||||
@ -1,11 +1,25 @@
|
|||||||
include ':app'
|
pluginManagement {
|
||||||
|
def flutterSdkPath = {
|
||||||
|
def properties = new Properties()
|
||||||
|
file("local.properties").withInputStream { properties.load(it) }
|
||||||
|
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||||
|
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||||
|
return flutterSdkPath
|
||||||
|
}()
|
||||||
|
|
||||||
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||||
def properties = new Properties()
|
|
||||||
|
|
||||||
assert localPropertiesFile.exists()
|
repositories {
|
||||||
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
plugins {
|
||||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
id "dev.flutter.flutter-plugin-loader" version "1.0.0" // apply true
|
||||||
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
|
id "com.android.application" version "8.5.1" apply false
|
||||||
|
id "org.jetbrains.kotlin.android" version "2.1.0" apply false
|
||||||
|
}
|
||||||
|
|
||||||
|
include ":app"
|
||||||
@ -13,18 +13,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: characters
|
name: characters
|
||||||
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.4.0"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.18.0"
|
version: "1.19.1"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -53,10 +53,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flame
|
name: flame
|
||||||
sha256: "2a2352741500ce47823dcf212f06b23e9bdb622454eab90244ee6da58e23b488"
|
sha256: f9e7a100c25f8d6bfd143bf325a9689c509216cd1c8133ce4684955c56770de7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.15.0"
|
version: "1.28.1"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -114,26 +114,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.0"
|
version: "0.11.1"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.12.0"
|
version: "1.16.0"
|
||||||
ordered_set:
|
ordered_set:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: ordered_set
|
name: ordered_set
|
||||||
sha256: "3fedcc9121b3ba24c0a84f32da2989c42e36c159b73feadbc2f402dc55966b81"
|
sha256: dc68b8f1abc7115b81cf890bf7d2ece4ed1d95e0f3e486ab4b64ab3d16d2ea42
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.1"
|
version: "7.0.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -154,7 +154,7 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.99"
|
version: "0.0.0"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -169,7 +169,7 @@ packages:
|
|||||||
path: ".."
|
path: ".."
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "4.2.34"
|
version: "4.2.35"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -211,5 +211,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.5"
|
version: "0.7.5"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.3.0-0 <4.0.0"
|
dart: ">=3.7.0-0 <4.0.0"
|
||||||
flutter: ">=3.16.0"
|
flutter: ">=3.27.1"
|
||||||
|
|||||||
@ -14,7 +14,7 @@ dependencies:
|
|||||||
spine_flutter:
|
spine_flutter:
|
||||||
path: ../
|
path: ../
|
||||||
cupertino_icons: ^1.0.6
|
cupertino_icons: ^1.0.6
|
||||||
flame: ^1.10.1
|
flame: ^1.28.1
|
||||||
raw_image_provider: ^0.2.0
|
raw_image_provider: ^0.2.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|||||||
Binary file not shown.
@ -1,6 +1,6 @@
|
|||||||
name: spine_flutter
|
name: spine_flutter
|
||||||
description: The official Spine Flutter Runtime to load, display and interact with Spine animations.
|
description: The official Spine Flutter Runtime to load, display and interact with Spine animations.
|
||||||
version: 4.2.35
|
version: 4.2.36
|
||||||
homepage: https://esotericsoftware.com
|
homepage: https://esotericsoftware.com
|
||||||
repository: https://github.com/esotericsoftware/spine-runtimes
|
repository: https://github.com/esotericsoftware/spine-runtimes
|
||||||
issue_tracker: https://github.com/esotericsoftware/spine-runtimes/issues
|
issue_tracker: https://github.com/esotericsoftware/spine-runtimes/issues
|
||||||
|
|||||||
@ -132,7 +132,6 @@ namespace Spine {
|
|||||||
float attachmentColorR, attachmentColorG, attachmentColorB, attachmentColorA;
|
float attachmentColorR, attachmentColorG, attachmentColorB, attachmentColorA;
|
||||||
object textureObject = null;
|
object textureObject = null;
|
||||||
int verticesCount = 0;
|
int verticesCount = 0;
|
||||||
float[] vertices = this.vertices;
|
|
||||||
int indicesCount = 0;
|
int indicesCount = 0;
|
||||||
int[] indices = null;
|
int[] indices = null;
|
||||||
float[] uvs = null;
|
float[] uvs = null;
|
||||||
@ -211,9 +210,10 @@ namespace Spine {
|
|||||||
darkColor.A = premultipliedAlpha ? (byte)255 : (byte)0;
|
darkColor.A = premultipliedAlpha ? (byte)255 : (byte)0;
|
||||||
|
|
||||||
// clip
|
// clip
|
||||||
|
float[] usedVertices = vertices;
|
||||||
if (clipper.IsClipping) {
|
if (clipper.IsClipping) {
|
||||||
clipper.ClipTriangles(vertices, indices, indicesCount, uvs);
|
clipper.ClipTriangles(usedVertices, indices, indicesCount, uvs);
|
||||||
vertices = clipper.ClippedVertices.Items;
|
usedVertices = clipper.ClippedVertices.Items;
|
||||||
verticesCount = clipper.ClippedVertices.Count >> 1;
|
verticesCount = clipper.ClippedVertices.Count >> 1;
|
||||||
indices = clipper.ClippedTriangles.Items;
|
indices = clipper.ClippedTriangles.Items;
|
||||||
indicesCount = clipper.ClippedTriangles.Count;
|
indicesCount = clipper.ClippedTriangles.Count;
|
||||||
@ -240,8 +240,8 @@ namespace Spine {
|
|||||||
for (int ii = 0, v = 0, nn = verticesCount << 1; v < nn; ii++, v += 2) {
|
for (int ii = 0, v = 0, nn = verticesCount << 1; v < nn; ii++, v += 2) {
|
||||||
itemVertices[ii].Color = color;
|
itemVertices[ii].Color = color;
|
||||||
itemVertices[ii].Color2 = darkColor;
|
itemVertices[ii].Color2 = darkColor;
|
||||||
itemVertices[ii].Position.X = vertices[v];
|
itemVertices[ii].Position.X = usedVertices[v];
|
||||||
itemVertices[ii].Position.Y = vertices[v + 1];
|
itemVertices[ii].Position.Y = usedVertices[v + 1];
|
||||||
itemVertices[ii].Position.Z = attachmentZOffset;
|
itemVertices[ii].Position.Z = attachmentZOffset;
|
||||||
itemVertices[ii].TextureCoordinate.X = uvs[v];
|
itemVertices[ii].TextureCoordinate.X = uvs[v];
|
||||||
itemVertices[ii].TextureCoordinate.Y = uvs[v + 1];
|
itemVertices[ii].TextureCoordinate.Y = uvs[v + 1];
|
||||||
|
|||||||
1840
spine-ts/package-lock.json
generated
1840
spine-ts/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@esotericsoftware/spine-ts",
|
"name": "@esotericsoftware/spine-ts",
|
||||||
"version": "4.2.81",
|
"version": "4.2.82",
|
||||||
"description": "The official Spine Runtimes for the web.",
|
"description": "The official Spine Runtimes for the web.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"files": [
|
"files": [
|
||||||
@ -85,9 +85,9 @@
|
|||||||
"@types/offscreencanvas": "^2019.6.4",
|
"@types/offscreencanvas": "^2019.6.4",
|
||||||
"concurrently": "^7.6.0",
|
"concurrently": "^7.6.0",
|
||||||
"copyfiles": "^2.4.1",
|
"copyfiles": "^2.4.1",
|
||||||
"esbuild": "^0.16.4",
|
"esbuild": "^0.25.4",
|
||||||
"live-server": "^1.2.2",
|
"alive-server": "^1.3.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"typescript": "5.6.2"
|
"typescript": "5.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ sed -i '' "s/$currentVersion/$newVersion/" spine-pixi-v8/package.json
|
|||||||
sed -i '' "s/$currentVersion/$newVersion/" spine-player/package.json
|
sed -i '' "s/$currentVersion/$newVersion/" spine-player/package.json
|
||||||
sed -i '' "s/$currentVersion/$newVersion/" spine-threejs/package.json
|
sed -i '' "s/$currentVersion/$newVersion/" spine-threejs/package.json
|
||||||
sed -i '' "s/$currentVersion/$newVersion/" spine-webgl/package.json
|
sed -i '' "s/$currentVersion/$newVersion/" spine-webgl/package.json
|
||||||
|
sed -i '' "s/$currentVersion/$newVersion/" spine-webcomponents/package.json
|
||||||
|
|
||||||
rm package-lock.json
|
rm package-lock.json
|
||||||
rm -rf node_modules/@esotericsoftware
|
rm -rf node_modules/@esotericsoftware
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@esotericsoftware/spine-canvas",
|
"name": "@esotericsoftware/spine-canvas",
|
||||||
"version": "4.2.81",
|
"version": "4.2.82",
|
||||||
"description": "The official Spine Runtimes for the web.",
|
"description": "The official Spine Runtimes for the web.",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
@ -31,6 +31,6 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@esotericsoftware/spine-core": "4.2.81"
|
"@esotericsoftware/spine-core": "4.2.82"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@esotericsoftware/spine-canvaskit",
|
"name": "@esotericsoftware/spine-canvaskit",
|
||||||
"version": "4.2.81",
|
"version": "4.2.82",
|
||||||
"description": "The official Spine Runtimes for CanvasKit for NodeJS",
|
"description": "The official Spine Runtimes for CanvasKit for NodeJS",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
@ -31,7 +31,7 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@esotericsoftware/spine-core": "4.2.81",
|
"@esotericsoftware/spine-core": "4.2.82",
|
||||||
"canvaskit-wasm": "0.39.1"
|
"canvaskit-wasm": "0.39.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@esotericsoftware/spine-core",
|
"name": "@esotericsoftware/spine-core",
|
||||||
"version": "4.2.81",
|
"version": "4.2.82",
|
||||||
"description": "The official Spine Runtimes for the web.",
|
"description": "The official Spine Runtimes for the web.",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@esotericsoftware/spine-phaser-v3",
|
"name": "@esotericsoftware/spine-phaser-v3",
|
||||||
"version": "4.2.81",
|
"version": "4.2.82",
|
||||||
"description": "The official Spine Runtimes for the Phaser v3.",
|
"description": "The official Spine Runtimes for the Phaser v3.",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
@ -31,9 +31,9 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@esotericsoftware/spine-canvas": "4.2.81",
|
"@esotericsoftware/spine-canvas": "4.2.82",
|
||||||
"@esotericsoftware/spine-core": "4.2.81",
|
"@esotericsoftware/spine-core": "4.2.82",
|
||||||
"@esotericsoftware/spine-webgl": "4.2.81"
|
"@esotericsoftware/spine-webgl": "4.2.82"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"phaser": "^3.60.0"
|
"phaser": "^3.60.0"
|
||||||
|
|||||||
@ -370,7 +370,10 @@ class SpineAtlasFile extends Phaser.Loader.MultiFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let basePath = file.src.match(/^.*\//) ?? "";
|
let basePath = (file.src.match(/^.*\//) ?? "").toString();
|
||||||
|
if (this.loader.path && this.loader.path.length > 0 && basePath.startsWith(this.loader.path))
|
||||||
|
basePath = basePath.slice(this.loader.path.length);
|
||||||
|
|
||||||
for (var i = 0; i < textures.length; i++) {
|
for (var i = 0; i < textures.length; i++) {
|
||||||
var url = basePath + textures[i];
|
var url = basePath + textures[i];
|
||||||
var key = file.key + "!" + textures[i];
|
var key = file.key + "!" + textures[i];
|
||||||
|
|||||||
@ -27,12 +27,9 @@
|
|||||||
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
declare global {
|
|
||||||
var require: any;
|
|
||||||
}
|
|
||||||
if (typeof window !== 'undefined' && window.Phaser) {
|
if (typeof window !== 'undefined' && window.Phaser) {
|
||||||
let prevRequire = window.require;
|
let prevRequire = window.require;
|
||||||
window.require = (x: string) => {
|
(window as any).require = (x: string) => {
|
||||||
if (prevRequire) return prevRequire(x);
|
if (prevRequire) return prevRequire(x);
|
||||||
else if (x === "Phaser") return window.Phaser;
|
else if (x === "Phaser") return window.Phaser;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@esotericsoftware/spine-phaser-v4",
|
"name": "@esotericsoftware/spine-phaser-v4",
|
||||||
"version": "4.2.81",
|
"version": "4.2.82",
|
||||||
"description": "The official Spine Runtimes for the Phaser v4.",
|
"description": "The official Spine Runtimes for the Phaser v4.",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
@ -31,9 +31,9 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@esotericsoftware/spine-canvas": "4.2.81",
|
"@esotericsoftware/spine-canvas": "4.2.82",
|
||||||
"@esotericsoftware/spine-core": "4.2.81",
|
"@esotericsoftware/spine-core": "4.2.82",
|
||||||
"@esotericsoftware/spine-webgl": "4.2.81"
|
"@esotericsoftware/spine-webgl": "4.2.82"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"phaser": "^4.0.0-rc.1"
|
"phaser": "^4.0.0-rc.1"
|
||||||
|
|||||||
@ -362,7 +362,10 @@ class SpineAtlasFile extends Phaser.Loader.MultiFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let basePath = file.src.match(/^.*\//) ?? "";
|
let basePath = (file.src.match(/^.*\//) ?? "").toString();
|
||||||
|
if (this.loader.path && this.loader.path.length > 0 && basePath.startsWith(this.loader.path))
|
||||||
|
basePath = basePath.slice(this.loader.path.length);
|
||||||
|
|
||||||
for (var i = 0; i < textures.length; i++) {
|
for (var i = 0; i < textures.length; i++) {
|
||||||
var url = basePath + textures[i];
|
var url = basePath + textures[i];
|
||||||
var key = file.key + "!" + textures[i];
|
var key = file.key + "!" + textures[i];
|
||||||
|
|||||||
@ -27,12 +27,9 @@
|
|||||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
declare global {
|
|
||||||
var require: any;
|
|
||||||
}
|
|
||||||
if (typeof window !== 'undefined' && window.Phaser) {
|
if (typeof window !== 'undefined' && window.Phaser) {
|
||||||
let prevRequire = window.require;
|
let prevRequire = window.require;
|
||||||
window.require = (x: string) => {
|
(window as any).require = (x: string) => {
|
||||||
if (prevRequire) return prevRequire(x);
|
if (prevRequire) return prevRequire(x);
|
||||||
else if (x === "Phaser") return window.Phaser;
|
else if (x === "Phaser") return window.Phaser;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@esotericsoftware/spine-pixi-v7",
|
"name": "@esotericsoftware/spine-pixi-v7",
|
||||||
"version": "4.2.81",
|
"version": "4.2.82",
|
||||||
"description": "The official Spine Runtimes for the web PixiJS v7.",
|
"description": "The official Spine Runtimes for the web PixiJS v7.",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
@ -31,7 +31,7 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@esotericsoftware/spine-core": "4.2.81"
|
"@esotericsoftware/spine-core": "4.2.82"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@pixi/core": "^7.2.4",
|
"@pixi/core": "^7.2.4",
|
||||||
|
|||||||
@ -27,16 +27,11 @@
|
|||||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
declare global {
|
if (typeof window !== 'undefined' && (window as any).PIXI) {
|
||||||
var require: any;
|
|
||||||
var PIXI: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof window !== 'undefined' && window.PIXI) {
|
|
||||||
let prevRequire = window.require;
|
let prevRequire = window.require;
|
||||||
window.require = (x: string) => {
|
(window as any).require = (x: string) => {
|
||||||
if (prevRequire) return prevRequire(x);
|
if (prevRequire) return prevRequire(x);
|
||||||
else if (x.startsWith("@pixi/")) return window.PIXI;
|
else if (x.startsWith("@pixi/")) return (window as any).PIXI;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@esotericsoftware/spine-pixi-v8",
|
"name": "@esotericsoftware/spine-pixi-v8",
|
||||||
"version": "4.2.81",
|
"version": "4.2.82",
|
||||||
"description": "The official Spine Runtimes for PixiJS v8.",
|
"description": "The official Spine Runtimes for PixiJS v8.",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
@ -31,7 +31,7 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@esotericsoftware/spine-core": "4.2.81"
|
"@esotericsoftware/spine-core": "4.2.82"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"pixi.js": "^8.4.0"
|
"pixi.js": "^8.4.0"
|
||||||
|
|||||||
@ -27,16 +27,11 @@
|
|||||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
declare global {
|
if (typeof window !== 'undefined' && (window as any).PIXI) {
|
||||||
var require: any;
|
|
||||||
var PIXI: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof window !== 'undefined' && window.PIXI) {
|
|
||||||
const prevRequire = window.require;
|
const prevRequire = window.require;
|
||||||
(window as any).require = (x: string) => {
|
(window as any).require = (x: string) => {
|
||||||
if (prevRequire) return prevRequire(x);
|
if (prevRequire) return prevRequire(x);
|
||||||
else if (x.startsWith("@pixi/") || x.startsWith("pixi.js")) return window.PIXI;
|
else if (x.startsWith("@pixi/") || x.startsWith("pixi.js")) return (window as any).PIXI;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@esotericsoftware/spine-player",
|
"name": "@esotericsoftware/spine-player",
|
||||||
"version": "4.2.81",
|
"version": "4.2.82",
|
||||||
"description": "The official Spine Runtimes for the web.",
|
"description": "The official Spine Runtimes for the web.",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
@ -31,6 +31,6 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@esotericsoftware/spine-webgl": "4.2.81"
|
"@esotericsoftware/spine-webgl": "4.2.82"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -151,6 +151,9 @@ export interface SpinePlayerConfig {
|
|||||||
filter settings from the texture atlas are used. Default: true */
|
filter settings from the texture atlas are used. Default: true */
|
||||||
mipmaps?: boolean
|
mipmaps?: boolean
|
||||||
|
|
||||||
|
/* Optional: Whether the player responds to user click/touch (play/pause, or control bones). Default: true */
|
||||||
|
interactive?: boolean
|
||||||
|
|
||||||
/* Optional: List of bone names that the user can drag to position. Default: none */
|
/* Optional: List of bone names that the user can drag to position. Default: none */
|
||||||
controlBones?: string[]
|
controlBones?: string[]
|
||||||
|
|
||||||
@ -239,6 +242,7 @@ export class SpinePlayer implements Disposable {
|
|||||||
private previousViewport: Viewport = {} as Viewport;
|
private previousViewport: Viewport = {} as Viewport;
|
||||||
private viewportTransitionStart = 0;
|
private viewportTransitionStart = 0;
|
||||||
private eventListeners: Array<{ target: any, event: any, func: any }> = [];
|
private eventListeners: Array<{ target: any, event: any, func: any }> = [];
|
||||||
|
private input?: Input;
|
||||||
|
|
||||||
constructor (parent: HTMLElement | string, private config: SpinePlayerConfig) {
|
constructor (parent: HTMLElement | string, private config: SpinePlayerConfig) {
|
||||||
let parentDom = typeof parent === "string" ? document.getElementById(parent) : parent;
|
let parentDom = typeof parent === "string" ? document.getElementById(parent) : parent;
|
||||||
@ -286,6 +290,7 @@ export class SpinePlayer implements Disposable {
|
|||||||
var eventListener = this.eventListeners[i];
|
var eventListener = this.eventListeners[i];
|
||||||
eventListener.target.removeEventListener(eventListener.event, eventListener.func);
|
eventListener.target.removeEventListener(eventListener.event, eventListener.func);
|
||||||
}
|
}
|
||||||
|
this.input?.dispose();
|
||||||
this.parent.removeChild(this.dom);
|
this.parent.removeChild(this.dom);
|
||||||
this.disposed = true;
|
this.disposed = true;
|
||||||
}
|
}
|
||||||
@ -312,6 +317,7 @@ export class SpinePlayer implements Disposable {
|
|||||||
if (config.premultipliedAlpha === void 0) config.premultipliedAlpha = true;
|
if (config.premultipliedAlpha === void 0) config.premultipliedAlpha = true;
|
||||||
if (config.preserveDrawingBuffer === void 0) config.preserveDrawingBuffer = false;
|
if (config.preserveDrawingBuffer === void 0) config.preserveDrawingBuffer = false;
|
||||||
if (config.mipmaps === void 0) config.mipmaps = true;
|
if (config.mipmaps === void 0) config.mipmaps = true;
|
||||||
|
if (config.interactive === void 0) config.interactive = true;
|
||||||
if (!config.debug) config.debug = {
|
if (!config.debug) config.debug = {
|
||||||
bones: false,
|
bones: false,
|
||||||
clipping: false,
|
clipping: false,
|
||||||
@ -592,57 +598,61 @@ export class SpinePlayer implements Disposable {
|
|||||||
let skeleton = this.skeleton!;
|
let skeleton = this.skeleton!;
|
||||||
let renderer = this.sceneRenderer!;
|
let renderer = this.sceneRenderer!;
|
||||||
|
|
||||||
let closest = function (x: number, y: number): Bone | null {
|
if (config.interactive) {
|
||||||
mouse.set(x, canvas.clientHeight - y, 0)
|
let closest = function (x: number, y: number): Bone | null {
|
||||||
offset.x = offset.y = 0;
|
mouse.set(x, canvas.clientHeight - y, 0)
|
||||||
let bestDistance = 24, index = 0;
|
offset.x = offset.y = 0;
|
||||||
let best: Bone | null = null;
|
let bestDistance = 24, index = 0;
|
||||||
for (let i = 0; i < controlBones.length; i++) {
|
let best: Bone | null = null;
|
||||||
selectedBones[i] = null;
|
for (let i = 0; i < controlBones.length; i++) {
|
||||||
let bone = skeleton.findBone(controlBones[i]);
|
selectedBones[i] = null;
|
||||||
if (!bone) continue;
|
let bone = skeleton.findBone(controlBones[i]);
|
||||||
let distance = renderer.camera.worldToScreen(
|
if (!bone) continue;
|
||||||
coords.set(bone.worldX, bone.worldY, 0),
|
let distance = renderer.camera.worldToScreen(
|
||||||
canvas.clientWidth, canvas.clientHeight).distance(mouse);
|
coords.set(bone.worldX, bone.worldY, 0),
|
||||||
if (distance < bestDistance) {
|
canvas.clientWidth, canvas.clientHeight).distance(mouse);
|
||||||
bestDistance = distance;
|
if (distance < bestDistance) {
|
||||||
best = bone;
|
bestDistance = distance;
|
||||||
index = i;
|
best = bone;
|
||||||
offset.x = coords.x - mouse.x;
|
index = i;
|
||||||
offset.y = coords.y - mouse.y;
|
offset.x = coords.x - mouse.x;
|
||||||
}
|
offset.y = coords.y - mouse.y;
|
||||||
}
|
|
||||||
if (best) selectedBones[index] = best;
|
|
||||||
return best;
|
|
||||||
};
|
|
||||||
|
|
||||||
new Input(canvas).addListener({
|
|
||||||
down: (x, y) => {
|
|
||||||
target = closest(x, y);
|
|
||||||
},
|
|
||||||
up: () => {
|
|
||||||
if (target)
|
|
||||||
target = null;
|
|
||||||
else if (config.showControls)
|
|
||||||
(this.paused ? this.play() : this.pause());
|
|
||||||
},
|
|
||||||
dragged: (x, y) => {
|
|
||||||
if (target) {
|
|
||||||
x = MathUtils.clamp(x + offset.x, 0, canvas.clientWidth)
|
|
||||||
y = MathUtils.clamp(y - offset.y, 0, canvas.clientHeight);
|
|
||||||
renderer.camera.screenToWorld(coords.set(x, y, 0), canvas.clientWidth, canvas.clientHeight);
|
|
||||||
if (target.parent) {
|
|
||||||
target.parent.worldToLocal(position.set(coords.x - skeleton.x, coords.y - skeleton.y));
|
|
||||||
target.x = position.x;
|
|
||||||
target.y = position.y;
|
|
||||||
} else {
|
|
||||||
target.x = coords.x - skeleton.x;
|
|
||||||
target.y = coords.y - skeleton.y;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
if (best) selectedBones[index] = best;
|
||||||
moved: (x, y) => closest(x, y)
|
return best;
|
||||||
});
|
};
|
||||||
|
|
||||||
|
this.input = new Input(canvas);
|
||||||
|
this.input.addListener({
|
||||||
|
down: (x, y) => {
|
||||||
|
target = closest(x, y);
|
||||||
|
},
|
||||||
|
up: () => {
|
||||||
|
if (target)
|
||||||
|
target = null;
|
||||||
|
else if (config.showControls)
|
||||||
|
(this.paused ? this.play() : this.pause());
|
||||||
|
},
|
||||||
|
dragged: (x, y) => {
|
||||||
|
if (target) {
|
||||||
|
x = MathUtils.clamp(x + offset.x, 0, canvas.clientWidth)
|
||||||
|
y = MathUtils.clamp(y - offset.y, 0, canvas.clientHeight);
|
||||||
|
renderer.camera.screenToWorld(coords.set(x, y, 0), canvas.clientWidth, canvas.clientHeight);
|
||||||
|
if (target.parent) {
|
||||||
|
target.parent.worldToLocal(position.set(coords.x - skeleton.x, coords.y - skeleton.y));
|
||||||
|
target.x = position.x;
|
||||||
|
target.y = position.y;
|
||||||
|
} else {
|
||||||
|
target.x = coords.x - skeleton.x;
|
||||||
|
target.y = coords.y - skeleton.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
moved: (x, y) => closest(x, y)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (config.showControls) {
|
if (config.showControls) {
|
||||||
// For manual hover to work, we need to disable hidding controls if the mouse/touch entered the clickable area of a child of the controls.
|
// For manual hover to work, we need to disable hidding controls if the mouse/touch entered the clickable area of a child of the controls.
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@esotericsoftware/spine-threejs",
|
"name": "@esotericsoftware/spine-threejs",
|
||||||
"version": "4.2.81",
|
"version": "4.2.82",
|
||||||
"description": "The official Spine Runtimes for the web.",
|
"description": "The official Spine Runtimes for the web.",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
@ -31,7 +31,7 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@esotericsoftware/spine-core": "4.2.81"
|
"@esotericsoftware/spine-core": "4.2.82"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/three": "0.162.0"
|
"@types/three": "0.162.0"
|
||||||
|
|||||||
@ -27,16 +27,11 @@
|
|||||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
declare global {
|
if (typeof window !== 'undefined' && (window as any).THREE) {
|
||||||
var require: any;
|
|
||||||
var THREE: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof window !== 'undefined' && window.THREE) {
|
|
||||||
let prevRequire = window.require;
|
let prevRequire = window.require;
|
||||||
window.require = (x: string) => {
|
(window as any).require = (x: string) => {
|
||||||
if (prevRequire) return prevRequire(x);
|
if (prevRequire) return prevRequire(x);
|
||||||
else if (x === "three") return window.THREE;
|
else if (x === "three") return (window as any).THREE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -231,11 +231,11 @@
|
|||||||
></spine-skeleton>
|
></spine-skeleton>
|
||||||
</div>
|
</div>
|
||||||
<div class="split-right">
|
<div class="split-right">
|
||||||
If you want to preserve the original scale, you can use <code>fit="none"</code>.
|
If you want to preserve the original scale, you can use <code>fit="none"</code> (center the bounds) or <code>fit="origin"</code> (center the skeleton origin).
|
||||||
In combination with that, you can use the <code>scale</code> attribute to set your desired scale.
|
In combination with that, you can use the <code>scale</code> attribute to set your desired scale.
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
Other fit modes are <code>width</code>, <code>height</code>, <code>cover</code>, and <code>scaleDown</code>.
|
Other fit modes are <code>width</code>, <code>height</code>, <code>cover</code>, <code>scaleDown</code>..
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -280,12 +280,20 @@
|
|||||||
|
|
||||||
<div class="split-top split">
|
<div class="split-top split">
|
||||||
<div class="split-left">
|
<div class="split-left">
|
||||||
|
<style>
|
||||||
|
.custom-class {
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
border: 1px solid green;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: -5px 5px 3px rgba(255, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<spine-skeleton
|
<spine-skeleton
|
||||||
atlas="/assets/spineboy-pma.atlas"
|
atlas="/assets/spineboy-pma.atlas"
|
||||||
skeleton="/assets/spineboy-pro.skel"
|
skeleton="/assets/spineboy-pro.skel"
|
||||||
animation="walk"
|
animation="walk"
|
||||||
height="150"
|
class="custom-class"
|
||||||
width="150"
|
|
||||||
></spine-skeleton>
|
></spine-skeleton>
|
||||||
<spine-skeleton
|
<spine-skeleton
|
||||||
atlas="/assets/spineboy-pma.atlas"
|
atlas="/assets/spineboy-pma.atlas"
|
||||||
@ -301,10 +309,9 @@
|
|||||||
></spine-skeleton>
|
></spine-skeleton>
|
||||||
</div>
|
</div>
|
||||||
<div class="split-right">
|
<div class="split-right">
|
||||||
If you want to manually size the Spine widget, specify the <code>width</code> and <code>height</code> attributes in pixels (without the "px" unit).
|
By default, the widget occupy zero width and height.
|
||||||
|
If you want to manually size the Spine widget, you can style the component using the <code>style</code> or <code>class</code> attribute, which provides more styling options.
|
||||||
<br>
|
<br>
|
||||||
<br>
|
|
||||||
If you prefer, you can style the component using the <code>style</code> attribute, which provides more styling options.
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -356,11 +363,6 @@
|
|||||||
|
|
||||||
<div class="split-top split">
|
<div class="split-top split">
|
||||||
<div class="split-left">
|
<div class="split-left">
|
||||||
The <code>origin</code> mode centers the animation's world origin with the center of the HTML element.
|
|
||||||
<br>
|
|
||||||
You are responsible for scaling the skeleton when using this mode.
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
Move the origin by a percentage of the div's width and height using the <code>x-axis</code> and <code>y-axis</code> attributes, respectively.
|
Move the origin by a percentage of the div's width and height using the <code>x-axis</code> and <code>y-axis</code> attributes, respectively.
|
||||||
</div>
|
</div>
|
||||||
<div class="split-right">
|
<div class="split-right">
|
||||||
@ -368,7 +370,7 @@
|
|||||||
atlas="/assets/vine-pma.atlas"
|
atlas="/assets/vine-pma.atlas"
|
||||||
skeleton="/assets/vine-pro.skel"
|
skeleton="/assets/vine-pro.skel"
|
||||||
animation="grow"
|
animation="grow"
|
||||||
mode="origin"
|
fit="origin"
|
||||||
scale=".5"
|
scale=".5"
|
||||||
y-axis="-.5"
|
y-axis="-.5"
|
||||||
></spine-skeleton>
|
></spine-skeleton>
|
||||||
@ -383,7 +385,7 @@
|
|||||||
atlas="/assets/vine-pma.atlas"
|
atlas="/assets/vine-pma.atlas"
|
||||||
skeleton="/assets/vine-pro.skel"
|
skeleton="/assets/vine-pro.skel"
|
||||||
animation="grow"
|
animation="grow"
|
||||||
mode="origin"
|
fit="origin"
|
||||||
scale=".5"
|
scale=".5"
|
||||||
y-axis="-.5"
|
y-axis="-.5"
|
||||||
></spine-skeleton>
|
></spine-skeleton>
|
||||||
@ -765,10 +767,11 @@
|
|||||||
<li><code>mixDuration</code>: the mix duration between this animation and the previous one (not used for the first animation on a track)</li>
|
<li><code>mixDuration</code>: the mix duration between this animation and the previous one (not used for the first animation on a track)</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<p>To loop a track once it reaches the end, add the special group <code>[loop, trackNumber]</code>, where:</p>
|
<p>To loop a track once it reaches the end, add the special group <code>[loop, trackNumber, repeatDelay]</code>, where:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>loop</code>: identifies this as a loop instruction</li>
|
<li><code>loop</code>: identifies this as a loop instruction</li>
|
||||||
<li><code>trackNumber</code>: the number of the track to loop</li>
|
<li><code>trackNumber</code>: the number of the track to loop</li>
|
||||||
|
<li><code>repeatDelay</code>: the number of seconds to wait after the last animation is completed before repeating the loop</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>The parameters of the first group on each track are passed to the <code>setAnimation</code> method, while the remaining groups use <code>addAnimation</code>.</p>
|
<p>The parameters of the first group on each track are passed to the <code>setAnimation</code> method, while the remaining groups use <code>addAnimation</code>.</p>
|
||||||
@ -1346,7 +1349,7 @@ function removeDiv() {
|
|||||||
Click the button below to toggle the spinner.
|
Click the button below to toggle the spinner.
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<input type="button" value="Spinner ON" onclick="toggleSpinner(this)">
|
<input type="button" value="Spinner OFF" onclick="toggleSpinner(this)">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -1355,7 +1358,6 @@ function removeDiv() {
|
|||||||
async function reloadWidget(element) {
|
async function reloadWidget(element) {
|
||||||
element.disabled = true;
|
element.disabled = true;
|
||||||
await widget.whenReady;
|
await widget.whenReady;
|
||||||
const skeleton = widget.skeleton;
|
|
||||||
widget.loading = true;
|
widget.loading = true;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
element.disabled = false;
|
element.disabled = false;
|
||||||
@ -1364,7 +1366,7 @@ function removeDiv() {
|
|||||||
}
|
}
|
||||||
function toggleSpinner(element) {
|
function toggleSpinner(element) {
|
||||||
widget.noSpinner = !widget.noSpinner;
|
widget.noSpinner = !widget.noSpinner;
|
||||||
element.value = widget.noSpinner ? "Spinner ON" : "Spinner OFF";
|
element.value = widget.noSpinner ? "Spinner OFF" : "Spinner ON";
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -1420,6 +1422,8 @@ function toggleSpinner(element) {
|
|||||||
|
|
||||||
<div class="split-left" style="width: 80%; box-sizing: border-box; min-height: 0;">
|
<div class="split-left" style="width: 80%; box-sizing: border-box; min-height: 0;">
|
||||||
It's very easy to display your different skins and animations. Simply create a table and use the <code>skin</code> and <code>animation</code> attributes.
|
It's very easy to display your different skins and animations. Simply create a table and use the <code>skin</code> and <code>animation</code> attributes.
|
||||||
|
<br>
|
||||||
|
<code>skin</code> accepts a comma separated list of skin names. The skins will be combined in a new one, from the first to the last. If multiple skins set the same slot, the latest in the list will be used.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="skin-grid">
|
<div class="skin-grid">
|
||||||
@ -2042,19 +2046,16 @@ skins.forEach((skin, i) => {
|
|||||||
<div class="split" style="width: 100%; flex-direction: column;">
|
<div class="split" style="width: 100%; flex-direction: column;">
|
||||||
|
|
||||||
<div class="split-left" style="width: 80%; box-sizing: border-box; min-height: 0;">
|
<div class="split-left" style="width: 80%; box-sizing: border-box; min-height: 0;">
|
||||||
When the widget (or the parent element) enters the viewport, the callback <code>onScreenFunction</code> is invoked.
|
When the widget (or its parent element) enters the viewport, two things happen:<br>
|
||||||
<br>
|
<ul>
|
||||||
<br>
|
<li>the widget's <code>onScreenAtLeastOnce</code> property is set to <code>true</code></li>
|
||||||
By default, the callback does two things:
|
<li>the widget's <code>onScreenFunction</code> callback is invoked</li>
|
||||||
<ul>
|
</ul>
|
||||||
<li>sets <code>onScreenAtLeastOnce</code> to <code>true</code> when the widget enters the viewport for the first time</li>
|
By default, <code>onScreenFunction</code> invokes the widget's <code>start</code> method if the widget has the <code>start-when-visible</code> attribute set, and this occurs only the first time it enters the viewport.<br>
|
||||||
<li>if <code>start-when-visible</code> is set, the widget's <code>start</code> method is invoked the first time the widget enters the viewport, and the assets are loaded at that moment.</li>
|
<br>
|
||||||
</ul>
|
The assets of the coin below are loaded only when the widget enters the viewport.<br>
|
||||||
<br>
|
<br>
|
||||||
The assets of the coin below are loaded only when the widget enters the viewport.
|
You can override the <code>onScreenFunction</code> behavior. For example, the raptor below changes its animation every time the widget enters the viewport.
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
You can overwrite the <code>onScreenFunction</code> behavior. For example, the raptor below changes its animation every time the widget enters the viewport.
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="skin-grid">
|
<div class="skin-grid">
|
||||||
@ -2077,7 +2078,6 @@ skins.forEach((skin, i) => {
|
|||||||
<script>
|
<script>
|
||||||
(async () => {
|
(async () => {
|
||||||
const raptorWidget = await spine.getSpineWidget("coin-with-raptor").whenReady;
|
const raptorWidget = await spine.getSpineWidget("coin-with-raptor").whenReady;
|
||||||
|
|
||||||
let raptorWalking = true;
|
let raptorWalking = true;
|
||||||
raptorWidget.onScreenFunction = widget => {
|
raptorWidget.onScreenFunction = widget => {
|
||||||
raptorWalking = !raptorWalking;
|
raptorWalking = !raptorWalking;
|
||||||
@ -2096,7 +2096,14 @@ skins.forEach((skin, i) => {
|
|||||||
<script>
|
<script>
|
||||||
escapeHTMLandInject(`
|
escapeHTMLandInject(`
|
||||||
<spine-skeleton
|
<spine-skeleton
|
||||||
identifier="coin"
|
atlas="/assets/coin-pma.atlas"
|
||||||
|
skeleton="/assets/coin-pro.skel"
|
||||||
|
animation="animation"
|
||||||
|
start-when-visible
|
||||||
|
></spine-skeleton>
|
||||||
|
|
||||||
|
<spine-skeleton
|
||||||
|
identifier="coin-with-raptor"
|
||||||
atlas="/assets/raptor-pma.atlas"
|
atlas="/assets/raptor-pma.atlas"
|
||||||
skeleton="/assets/raptor-pro.skel"
|
skeleton="/assets/raptor-pro.skel"
|
||||||
animation="walk"
|
animation="walk"
|
||||||
@ -2689,8 +2696,6 @@ tank.beforeUpdateWorldTransforms = (delta, skeleton, state) => {
|
|||||||
<div class="split-left" style="overflow-y: auto; width: 100px; height: 200px; transform: translateZ(0);">
|
<div class="split-left" style="overflow-y: auto; width: 100px; height: 200px; transform: translateZ(0);">
|
||||||
<spine-overlay
|
<spine-overlay
|
||||||
overlay-id="scroll"
|
overlay-id="scroll"
|
||||||
scrollable
|
|
||||||
no-auto-parent-transform
|
|
||||||
overflow-top=".2"
|
overflow-top=".2"
|
||||||
overflow-bottom=".2"
|
overflow-bottom=".2"
|
||||||
overflow-left=".2"
|
overflow-left=".2"
|
||||||
@ -3271,7 +3276,7 @@ const darkPicker = document.getElementById("dark-picker");
|
|||||||
<li><code>followOpacity</code>: the element opacity is connected to the slot alpha</li>
|
<li><code>followOpacity</code>: the element opacity is connected to the slot alpha</li>
|
||||||
<li><code>followScale</code>: the element scale is connected to the slot scale</li>
|
<li><code>followScale</code>: the element scale is connected to the slot scale</li>
|
||||||
<li><code>followRotation</code>: the element rotation is connected to the slot rotation</li>
|
<li><code>followRotation</code>: the element rotation is connected to the slot rotation</li>
|
||||||
<li><code>followAttachmentAttach</code>: the element is shown/hidden depending if the slot contains an attachment or not</li>
|
<li><code>followVisibility</code>: the element is shown/hidden depending if the slot contains an attachment or not</li>
|
||||||
<li><code>hideAttachment</code>: the slot attachment is hidden as if the element replaced the attachment</li>
|
<li><code>hideAttachment</code>: the slot attachment is hidden as if the element replaced the attachment</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
@ -3296,10 +3301,10 @@ const darkPicker = document.getElementById("dark-picker");
|
|||||||
<script>
|
<script>
|
||||||
(async () => {
|
(async () => {
|
||||||
const widget = await spine.getSpineWidget("potty").whenReady;
|
const widget = await spine.getSpineWidget("potty").whenReady;
|
||||||
widget.followSlot("rain/rain-color", document.getElementById("rain/rain-color"), { followAttachmentAttach: false, hideAttachment: true });
|
widget.followSlot("rain/rain-color", document.getElementById("rain/rain-color"), { followVisibility: false, hideAttachment: true });
|
||||||
widget.followSlot("rain/rain-white", document.getElementById("rain/rain-white"), { followAttachmentAttach: false, hideAttachment: true });
|
widget.followSlot("rain/rain-white", document.getElementById("rain/rain-white"), { followVisibility: false, hideAttachment: true });
|
||||||
widget.followSlot("rain/rain-blue", document.getElementById("rain/rain-blue"), { followAttachmentAttach: false, hideAttachment: true });
|
widget.followSlot("rain/rain-blue", document.getElementById("rain/rain-blue"), { followVisibility: false, hideAttachment: true });
|
||||||
widget.followSlot("rain/rain-green", document.getElementById("rain/rain-green"), { followAttachmentAttach: false, hideAttachment: true });
|
widget.followSlot("rain/rain-green", document.getElementById("rain/rain-green"), { followVisibility: false, hideAttachment: true });
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -3323,10 +3328,10 @@ const darkPicker = document.getElementById("dark-picker");
|
|||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const widget = await spine.getSpineWidget("potty").whenReady;
|
const widget = await spine.getSpineWidget("potty").whenReady;
|
||||||
widget.followSlot("rain/rain-color", document.getElementById("rain/rain-color"), { followAttachmentAttach: false, hideAttachment: true });
|
widget.followSlot("rain/rain-color", document.getElementById("rain/rain-color"), { followVisibility: false, hideAttachment: true });
|
||||||
widget.followSlot("rain/rain-white", document.getElementById("rain/rain-white"), { followAttachmentAttach: false, hideAttachment: true });
|
widget.followSlot("rain/rain-white", document.getElementById("rain/rain-white"), { followVisibility: false, hideAttachment: true });
|
||||||
widget.followSlot("rain/rain-blue", document.getElementById("rain/rain-blue"), { followAttachmentAttach: false, hideAttachment: true });
|
widget.followSlot("rain/rain-blue", document.getElementById("rain/rain-blue"), { followVisibility: false, hideAttachment: true });
|
||||||
widget.followSlot("rain/rain-green", document.getElementById("rain/rain-green"), { followAttachmentAttach: false, hideAttachment: true });
|
widget.followSlot("rain/rain-green", document.getElementById("rain/rain-green"), { followVisibility: false, hideAttachment: true });
|
||||||
})();`);</script>
|
})();`);</script>
|
||||||
</code></pre>
|
</code></pre>
|
||||||
</div>
|
</div>
|
||||||
@ -3370,10 +3375,10 @@ const darkPicker = document.getElementById("dark-picker");
|
|||||||
<script>
|
<script>
|
||||||
(async () => {
|
(async () => {
|
||||||
const widget = await spine.getSpineWidget("potty2").whenReady;
|
const widget = await spine.getSpineWidget("potty2").whenReady;
|
||||||
widget.followSlot("rain/rain-color", spine.getSpineWidget("potty2-1"), { followAttachmentAttach: false, hideAttachment: true });
|
widget.followSlot("rain/rain-color", spine.getSpineWidget("potty2-1"), { followVisibility: false, hideAttachment: true });
|
||||||
widget.followSlot("rain/rain-white", spine.getSpineWidget("potty2-2"), { followAttachmentAttach: false, hideAttachment: true });
|
widget.followSlot("rain/rain-white", spine.getSpineWidget("potty2-2"), { followVisibility: false, hideAttachment: true });
|
||||||
widget.followSlot("rain/rain-blue", spine.getSpineWidget("potty2-3"), { followAttachmentAttach: false, hideAttachment: true });
|
widget.followSlot("rain/rain-blue", spine.getSpineWidget("potty2-3"), { followVisibility: false, hideAttachment: true });
|
||||||
widget.followSlot("rain/rain-green", spine.getSpineWidget("potty2-4"), { followAttachmentAttach: false, hideAttachment: true });
|
widget.followSlot("rain/rain-green", spine.getSpineWidget("potty2-4"), { followVisibility: false, hideAttachment: true });
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -3397,10 +3402,10 @@ const darkPicker = document.getElementById("dark-picker");
|
|||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const widget = await spine.getSpineWidget("potty2").whenReady;
|
const widget = await spine.getSpineWidget("potty2").whenReady;
|
||||||
widget.followSlot("rain/rain-color", spine.getSpineWidget("potty2-1"), { followAttachmentAttach: false, hideAttachment: true });
|
widget.followSlot("rain/rain-color", spine.getSpineWidget("potty2-1"), { followVisibility: false, hideAttachment: true });
|
||||||
widget.followSlot("rain/rain-white", spine.getSpineWidget("potty2-2"), { followAttachmentAttach: false, hideAttachment: true });
|
widget.followSlot("rain/rain-white", spine.getSpineWidget("potty2-2"), { followVisibility: false, hideAttachment: true });
|
||||||
widget.followSlot("rain/rain-blue", spine.getSpineWidget("potty2-3"), { followAttachmentAttach: false, hideAttachment: true });
|
widget.followSlot("rain/rain-blue", spine.getSpineWidget("potty2-3"), { followVisibility: false, hideAttachment: true });
|
||||||
widget.followSlot("rain/rain-green", spine.getSpineWidget("potty2-4"), { followAttachmentAttach: false, hideAttachment: true });
|
widget.followSlot("rain/rain-green", spine.getSpineWidget("potty2-4"), { followVisibility: false, hideAttachment: true });
|
||||||
})();`);</script>
|
})();`);</script>
|
||||||
</code></pre>
|
</code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@esotericsoftware/spine-webcomponents",
|
"name": "@esotericsoftware/spine-webcomponents",
|
||||||
"version": "4.2.80",
|
"version": "4.2.82",
|
||||||
"description": "The official Spine webcomponents.",
|
"description": "The official Spine webcomponents.",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
@ -31,6 +31,6 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@esotericsoftware/spine-webgl": "4.2.80"
|
"@esotericsoftware/spine-webgl": "4.2.82"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,6 +149,8 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
private lastCanvasBaseWidth = 0;
|
private lastCanvasBaseWidth = 0;
|
||||||
private lastCanvasBaseHeight = 0;
|
private lastCanvasBaseHeight = 0;
|
||||||
|
|
||||||
|
private zIndex?: number;
|
||||||
|
|
||||||
private disposed = false;
|
private disposed = false;
|
||||||
private loaded = false;
|
private loaded = false;
|
||||||
|
|
||||||
@ -164,13 +166,14 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
*
|
*
|
||||||
* In order to fix this behaviour, it is necessary to insert a dedicated `spine-overlay` webcomponent as a direct child of the container.
|
* In order to fix this behaviour, it is necessary to insert a dedicated `spine-overlay` webcomponent as a direct child of the container.
|
||||||
* Moreover, it is necessary to perform the following actions:
|
* Moreover, it is necessary to perform the following actions:
|
||||||
* 1) The scrollable container must have a `transform` css attribute. If it hasn't this attribute the `spine-overlay` will add it for you.
|
* 1) The appendedToBody container must have a `transform` css attribute. If it hasn't this attribute the `spine-overlay` will add it for you.
|
||||||
* If your scrollable container has already this css attribute, or if you prefer to add it by yourself (example: `transform: translateZ(0);`), set the `no-auto-parent-transform` to the `spine-overlay`.
|
* If your appendedToBody container has already this css attribute, or if you prefer to add it by yourself (example: `transform: translateZ(0);`), set the `no-auto-parent-transform` to the `spine-overlay`.
|
||||||
* 2) The `spine-overlay` must have an `overlay-id` attribute. Choose the value you prefer.
|
* 2) The `spine-overlay` must have an `overlay-id` attribute. Choose the value you prefer.
|
||||||
* 3) Each `spine-skeleton` must have an `overlay-id` attribute. The same as the hosting `spine-overlay`.
|
* 3) Each `spine-skeleton` must have an `overlay-id` attribute. The same as the hosting `spine-overlay`.
|
||||||
* Connected to `scrollable` attribute.
|
* Connected to `appendedToBody` attribute.
|
||||||
*/
|
*/
|
||||||
private appendedToBody = true;
|
private appendedToBody = true;
|
||||||
|
private hasParentTransform = true;
|
||||||
|
|
||||||
readonly time = new TimeKeeper();
|
readonly time = new TimeKeeper();
|
||||||
|
|
||||||
@ -257,6 +260,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
widget.onScreen = isIntersecting;
|
widget.onScreen = isIntersecting;
|
||||||
if (isIntersecting) {
|
if (isIntersecting) {
|
||||||
widget.onScreenFunction(widget);
|
widget.onScreenFunction(widget);
|
||||||
|
widget.onScreenAtLeastOnce = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -267,8 +271,10 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
// Alternatively, we can store the body size, check the current body size in the loop (like the translateCanvas), and
|
// Alternatively, we can store the body size, check the current body size in the loop (like the translateCanvas), and
|
||||||
// if they differs call the resizeCallback. I already tested it, and it works. ResizeObserver should be more efficient.
|
// if they differs call the resizeCallback. I already tested it, and it works. ResizeObserver should be more efficient.
|
||||||
if (this.appendedToBody) {
|
if (this.appendedToBody) {
|
||||||
// if the element is scrollable, the user does not disable translate tweak, and the parent did not have already a transform, add the tweak
|
// if the element is appendedToBody, the user does not disable translate tweak, and the parent did not have already a transform, add the tweak
|
||||||
if (this.appendedToBody && !this.noAutoParentTransform && getComputedStyle(this.parentElement!).transform === "none") {
|
if (this.hasCssTweakOff()) {
|
||||||
|
this.hasParentTransform = false;
|
||||||
|
} else {
|
||||||
this.parentElement!.style.transform = `translateZ(0)`;
|
this.parentElement!.style.transform = `translateZ(0)`;
|
||||||
}
|
}
|
||||||
this.resizeObserver = new ResizeObserver(this.resizedCallback);
|
this.resizeObserver = new ResizeObserver(this.resizedCallback);
|
||||||
@ -376,6 +382,8 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
this.parentElement!.appendChild(this);
|
this.parentElement!.appendChild(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.updateZIndexIfNecessary(widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -475,7 +483,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
|
|
||||||
const tempVector = new Vector3();
|
const tempVector = new Vector3();
|
||||||
for (const widget of this.widgets) {
|
for (const widget of this.widgets) {
|
||||||
const { skeleton, pma, bounds, mode, debug, offsetX, offsetY, xAxis, yAxis, dragX, dragY, fit, noSpinner, onScreen, loading, clip, isDraggable } = widget;
|
const { skeleton, pma, bounds, debug, offsetX, offsetY, dragX, dragY, fit, noSpinner, loading, clip, isDraggable } = widget;
|
||||||
|
|
||||||
if (widget.isOffScreenAndWasMoved()) continue;
|
if (widget.isOffScreenAndWasMoved()) continue;
|
||||||
const elementRef = widget.getHostElement();
|
const elementRef = widget.getHostElement();
|
||||||
@ -489,7 +497,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
divBounds.y -= offsetTopForOverlay;
|
divBounds.y -= offsetTopForOverlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { padLeft, padRight, padTop, padBottom } = widget
|
const { padLeft, padRight, padTop, padBottom, xAxis, yAxis } = widget
|
||||||
const paddingShiftHorizontal = (padLeft - padRight) / 2;
|
const paddingShiftHorizontal = (padLeft - padRight) / 2;
|
||||||
const paddingShiftVertical = (padTop - padBottom) / 2;
|
const paddingShiftVertical = (padTop - padBottom) / 2;
|
||||||
|
|
||||||
@ -508,7 +516,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
if (clip) startScissor(divBounds);
|
if (clip) startScissor(divBounds);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
if (noSpinner) {
|
if (!noSpinner) {
|
||||||
if (!widget.loadingScreen) widget.loadingScreen = new LoadingScreen(renderer);
|
if (!widget.loadingScreen) widget.loadingScreen = new LoadingScreen(renderer);
|
||||||
widget.loadingScreen!.drawInCoordinates(divOriginX, divOriginY);
|
widget.loadingScreen!.drawInCoordinates(divOriginX, divOriginY);
|
||||||
}
|
}
|
||||||
@ -517,7 +525,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (skeleton) {
|
if (skeleton) {
|
||||||
if (mode === "inside") {
|
if (fit !== "origin") {
|
||||||
let { x: ax, y: ay, width: aw, height: ah } = bounds;
|
let { x: ax, y: ay, width: aw, height: ah } = bounds;
|
||||||
if (aw <= 0 || ah <= 0) continue;
|
if (aw <= 0 || ah <= 0) continue;
|
||||||
|
|
||||||
@ -583,8 +591,9 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const worldOffsetX = divOriginX + offsetX + dragX;
|
// const worldOffsetX = divOriginX + offsetX + dragX;
|
||||||
const worldOffsetY = divOriginY + offsetY + dragY;
|
const worldOffsetX = divOriginX + offsetX * window.devicePixelRatio + dragX;
|
||||||
|
const worldOffsetY = divOriginY + offsetY * window.devicePixelRatio + dragY;
|
||||||
|
|
||||||
widget.worldX = worldOffsetX;
|
widget.worldX = worldOffsetX;
|
||||||
widget.worldY = worldOffsetY;
|
widget.worldY = worldOffsetY;
|
||||||
@ -626,12 +635,10 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
renderer.circle(true, root.x + worldOffsetX, root.y + worldOffsetY, 10, red);
|
renderer.circle(true, root.x + worldOffsetX, root.y + worldOffsetY, 10, red);
|
||||||
|
|
||||||
// show shifted origin
|
// show shifted origin
|
||||||
const originX = worldOffsetX - dragX - offsetX;
|
renderer.circle(true, divOriginX, divOriginY, 10, green);
|
||||||
const originY = worldOffsetY - dragY - offsetY;
|
|
||||||
renderer.circle(true, originX, originY, 10, green);
|
|
||||||
|
|
||||||
// show line from origin to bounds center
|
// show line from origin to bounds center
|
||||||
renderer.line(originX, originY, bbCenterX, bbCenterY, green);
|
renderer.line(divOriginX, divOriginY, bbCenterX, bbCenterY, green);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clip) endScissor();
|
if (clip) endScissor();
|
||||||
@ -646,7 +653,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
if (widget.isOffScreenAndWasMoved() || !widget.skeleton) continue;
|
if (widget.isOffScreenAndWasMoved() || !widget.skeleton) continue;
|
||||||
|
|
||||||
for (const boneFollower of widget.boneFollowerList) {
|
for (const boneFollower of widget.boneFollowerList) {
|
||||||
const { slot, bone, element, followAttachmentAttach, followRotation, followOpacity, followScale } = boneFollower;
|
const { slot, bone, element, followVisibility, followRotation, followOpacity, followScale } = boneFollower;
|
||||||
const { worldX, worldY } = widget;
|
const { worldX, worldY } = widget;
|
||||||
this.worldToScreen(this.tempFollowBoneVector, bone.worldX + worldX, bone.worldY + worldY);
|
this.worldToScreen(this.tempFollowBoneVector, bone.worldX + worldX, bone.worldY + worldY);
|
||||||
|
|
||||||
@ -667,7 +674,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
|
|
||||||
element.style.display = ""
|
element.style.display = ""
|
||||||
|
|
||||||
if (followAttachmentAttach && !slot.attachment) {
|
if (followVisibility && !slot.attachment) {
|
||||||
element.style.opacity = "0";
|
element.style.opacity = "0";
|
||||||
} else if (followOpacity) {
|
} else if (followOpacity) {
|
||||||
element.style.opacity = `${slot.color.a}`;
|
element.style.opacity = `${slot.color.a}`;
|
||||||
@ -943,7 +950,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
private updateWidgetScales () {
|
private updateWidgetScales () {
|
||||||
for (const widget of this.widgets) {
|
for (const widget of this.widgets) {
|
||||||
// inside mode scale automatically to fit the skeleton within its parent
|
// inside mode scale automatically to fit the skeleton within its parent
|
||||||
if (widget.mode !== "origin" && widget.fit !== "none") continue;
|
if (widget.fit !== "origin" && widget.fit !== "none") continue;
|
||||||
|
|
||||||
const skeleton = widget.skeleton;
|
const skeleton = widget.skeleton;
|
||||||
if (!skeleton) continue;
|
if (!skeleton) continue;
|
||||||
@ -958,6 +965,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this function is invoked each frame - pay attention to what you add here
|
||||||
private translateCanvas () {
|
private translateCanvas () {
|
||||||
let scrollPositionX = -this.overflowLeftSize;
|
let scrollPositionX = -this.overflowLeftSize;
|
||||||
let scrollPositionY = -this.overflowTopSize;
|
let scrollPositionY = -this.overflowTopSize;
|
||||||
@ -967,9 +975,9 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
scrollPositionY += window.scrollY;
|
scrollPositionY += window.scrollY;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Ideally this should be the only scrollable case (no-auto-parent-transform not enabled or at least an ancestor has transform)
|
// Ideally this should be the only appendedToBody case (no-auto-parent-transform not enabled or at least an ancestor has transform)
|
||||||
// I'd like to get rid of the code below
|
// I'd like to get rid of the else case
|
||||||
if (!this.hasCssTweakOff()) {
|
if (this.hasParentTransform) {
|
||||||
scrollPositionX += this.parentElement!.scrollLeft;
|
scrollPositionX += this.parentElement!.scrollLeft;
|
||||||
scrollPositionY += this.parentElement!.scrollTop;
|
scrollPositionY += this.parentElement!.scrollTop;
|
||||||
} else {
|
} else {
|
||||||
@ -979,7 +987,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
|
|
||||||
let offsetParent = this.offsetParent;
|
let offsetParent = this.offsetParent;
|
||||||
do {
|
do {
|
||||||
if (offsetParent === document.body) break;
|
if (offsetParent === null || offsetParent === document.body) break;
|
||||||
|
|
||||||
const htmlOffsetParentElement = offsetParent as HTMLElement;
|
const htmlOffsetParentElement = offsetParent as HTMLElement;
|
||||||
if (htmlOffsetParentElement.style.position === "fixed" || htmlOffsetParentElement.style.position === "sticky" || htmlOffsetParentElement.style.position === "absolute") {
|
if (htmlOffsetParentElement.style.position === "fixed" || htmlOffsetParentElement.style.position === "sticky" || htmlOffsetParentElement.style.position === "absolute") {
|
||||||
@ -1000,6 +1008,23 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
this.canvas.style.transform = `translate(${scrollPositionX}px,${scrollPositionY}px)`;
|
this.canvas.style.transform = `translate(${scrollPositionX}px,${scrollPositionY}px)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateZIndexIfNecessary (element: HTMLElement) {
|
||||||
|
let parent: HTMLElement | null = element;
|
||||||
|
let zIndex: undefined | number;
|
||||||
|
do {
|
||||||
|
let currentZIndex = parseInt(getComputedStyle(parent).zIndex);
|
||||||
|
|
||||||
|
// searching the shallowest z-index
|
||||||
|
if (!isNaN(currentZIndex)) zIndex = currentZIndex;
|
||||||
|
parent = parent.parentElement;
|
||||||
|
} while (parent && parent !== document.body)
|
||||||
|
|
||||||
|
if (zIndex && (!this.zIndex || this.zIndex < zIndex)) {
|
||||||
|
this.zIndex = zIndex;
|
||||||
|
this.div.style.zIndex = `${this.zIndex}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Other utilities
|
* Other utilities
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -49,6 +49,7 @@ import {
|
|||||||
RegionAttachment,
|
RegionAttachment,
|
||||||
MeshAttachment,
|
MeshAttachment,
|
||||||
Bone,
|
Bone,
|
||||||
|
Skin,
|
||||||
} from "@esotericsoftware/spine-webgl";
|
} from "@esotericsoftware/spine-webgl";
|
||||||
import { AttributeTypes, castValue, isBase64, Rectangle } from "./wcUtils.js";
|
import { AttributeTypes, castValue, isBase64, Rectangle } from "./wcUtils.js";
|
||||||
import { SpineWebComponentOverlay } from "./SpineWebComponentOverlay.js";
|
import { SpineWebComponentOverlay } from "./SpineWebComponentOverlay.js";
|
||||||
@ -56,9 +57,12 @@ import { SpineWebComponentOverlay } from "./SpineWebComponentOverlay.js";
|
|||||||
type UpdateSpineWidgetFunction = (delta: number, skeleton: Skeleton, state: AnimationState) => void;
|
type UpdateSpineWidgetFunction = (delta: number, skeleton: Skeleton, state: AnimationState) => void;
|
||||||
|
|
||||||
export type OffScreenUpdateBehaviourType = "pause" | "update" | "pose";
|
export type OffScreenUpdateBehaviourType = "pause" | "update" | "pose";
|
||||||
export type ModeType = "inside" | "origin";
|
export type FitType = "fill" | "width" | "height" | "contain" | "cover" | "none" | "scaleDown" | "origin";
|
||||||
export type FitType = "fill" | "width" | "height" | "contain" | "cover" | "none" | "scaleDown";
|
export type AnimationsInfo = Record<string, {
|
||||||
export type AnimationsInfo = Record<string, { cycle?: boolean, animations: Array<AnimationsType> }>;
|
cycle?: boolean,
|
||||||
|
repeatDelay?: number;
|
||||||
|
animations: Array<AnimationsType>
|
||||||
|
}>;
|
||||||
export type AnimationsType = { animationName: string | "#EMPTY#", loop?: boolean, delay?: number, mixDuration?: number };
|
export type AnimationsType = { animationName: string | "#EMPTY#", loop?: boolean, delay?: number, mixDuration?: number };
|
||||||
export type CursorEventType = "down" | "up" | "enter" | "leave" | "move" | "drag";
|
export type CursorEventType = "down" | "up" | "enter" | "leave" | "move" | "drag";
|
||||||
export type CursorEventTypesInput = Exclude<CursorEventType, "enter" | "leave">;
|
export type CursorEventTypesInput = Exclude<CursorEventType, "enter" | "leave">;
|
||||||
@ -73,9 +77,8 @@ interface WidgetAttributes {
|
|||||||
animation?: string
|
animation?: string
|
||||||
animations?: AnimationsInfo
|
animations?: AnimationsInfo
|
||||||
defaultMix?: number
|
defaultMix?: number
|
||||||
skin?: string
|
skin?: string[]
|
||||||
fit: FitType
|
fit: FitType
|
||||||
mode: ModeType
|
|
||||||
xAxis: number
|
xAxis: number
|
||||||
yAxis: number
|
yAxis: number
|
||||||
offsetX: number
|
offsetX: number
|
||||||
@ -217,14 +220,14 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
|
|||||||
* Optional: The name of the skin to be set
|
* Optional: The name of the skin to be set
|
||||||
* Connected to `skin` attribute.
|
* Connected to `skin` attribute.
|
||||||
*/
|
*/
|
||||||
public get skin (): string | undefined {
|
public get skin (): string[] | undefined {
|
||||||
return this._skin;
|
return this._skin;
|
||||||
}
|
}
|
||||||
public set skin (value: string | undefined) {
|
public set skin (value: string[] | undefined) {
|
||||||
this._skin = value;
|
this._skin = value;
|
||||||
this.initWidget();
|
this.initWidget();
|
||||||
}
|
}
|
||||||
private _skin?: string
|
private _skin?: string[]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify the way the skeleton is sized within the element automatically changing its `scaleX` and `scaleY`.
|
* Specify the way the skeleton is sized within the element automatically changing its `scaleX` and `scaleY`.
|
||||||
@ -236,19 +239,11 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
|
|||||||
* - `cover`: as small as possible while still covering the entire element container.
|
* - `cover`: as small as possible while still covering the entire element container.
|
||||||
* - `scaleDown`: scale the skeleton down to ensure that the skeleton fits within the element container.
|
* - `scaleDown`: scale the skeleton down to ensure that the skeleton fits within the element container.
|
||||||
* - `none`: display the skeleton without autoscaling it.
|
* - `none`: display the skeleton without autoscaling it.
|
||||||
|
* - `origin`: the skeleton origin is centered with the element container regardless of the bounds.
|
||||||
* Connected to `fit` attribute.
|
* Connected to `fit` attribute.
|
||||||
*/
|
*/
|
||||||
public fit: FitType = "contain";
|
public fit: FitType = "contain";
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the way the skeleton is centered within the element container:
|
|
||||||
* - `inside`: the skeleton bounds center is centered with the element container (Default)
|
|
||||||
* - `origin`: the skeleton origin is centered with the element container regardless of the bounds.
|
|
||||||
* Origin does not allow to specify any {@link fit} type and guarantee the skeleton to not be autoscaled.
|
|
||||||
* Connected to `mode` attribute.
|
|
||||||
*/
|
|
||||||
public mode: ModeType = "inside";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The x offset of the skeleton world origin x axis as a percentage of the element container width
|
* The x offset of the skeleton world origin x axis as a percentage of the element container width
|
||||||
* Connected to `x-axis` attribute.
|
* Connected to `x-axis` attribute.
|
||||||
@ -562,16 +557,11 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
|
|||||||
/**
|
/**
|
||||||
* A callback invoked each time the element container enters the screen viewport.
|
* A callback invoked each time the element container enters the screen viewport.
|
||||||
* By default, the callback call the {@link start} method the first time the widget
|
* By default, the callback call the {@link start} method the first time the widget
|
||||||
* enters the screen viewport.
|
* enters the screen viewport and {@link startWhenVisible} is `true`.
|
||||||
*/
|
*/
|
||||||
public onScreenFunction: (widget: SpineWebComponentSkeleton) => void = async (widget) => {
|
public onScreenFunction: (widget: SpineWebComponentSkeleton) => void = async (widget) => {
|
||||||
if (widget.loading && !widget.onScreenAtLeastOnce) {
|
if (widget.loading && !widget.onScreenAtLeastOnce && widget.manualStart && widget.startWhenVisible)
|
||||||
widget.onScreenAtLeastOnce = true;
|
widget.start()
|
||||||
|
|
||||||
if (widget.manualStart && widget.startWhenVisible) {
|
|
||||||
widget.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -706,7 +696,7 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
|
|||||||
animations: { propertyName: "animations", type: "animationsInfo", defaultValue: undefined },
|
animations: { propertyName: "animations", type: "animationsInfo", defaultValue: undefined },
|
||||||
"animation-bounds": { propertyName: "animationsBound", type: "array-string", defaultValue: undefined },
|
"animation-bounds": { propertyName: "animationsBound", type: "array-string", defaultValue: undefined },
|
||||||
"default-mix": { propertyName: "defaultMix", type: "number", defaultValue: 0 },
|
"default-mix": { propertyName: "defaultMix", type: "number", defaultValue: 0 },
|
||||||
skin: { propertyName: "skin", type: "string" },
|
skin: { propertyName: "skin", type: "array-string" },
|
||||||
width: { propertyName: "width", type: "number", defaultValue: -1 },
|
width: { propertyName: "width", type: "number", defaultValue: -1 },
|
||||||
height: { propertyName: "height", type: "number", defaultValue: -1 },
|
height: { propertyName: "height", type: "number", defaultValue: -1 },
|
||||||
isdraggable: { propertyName: "isDraggable", type: "boolean" },
|
isdraggable: { propertyName: "isDraggable", type: "boolean" },
|
||||||
@ -732,7 +722,6 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
|
|||||||
clip: { propertyName: "clip", type: "boolean" },
|
clip: { propertyName: "clip", type: "boolean" },
|
||||||
pages: { propertyName: "pages", type: "array-number" },
|
pages: { propertyName: "pages", type: "array-number" },
|
||||||
fit: { propertyName: "fit", type: "fitType", defaultValue: "contain" },
|
fit: { propertyName: "fit", type: "fitType", defaultValue: "contain" },
|
||||||
mode: { propertyName: "mode", type: "modeType", defaultValue: "inside" },
|
|
||||||
offscreen: { propertyName: "offScreenUpdateBehaviour", type: "offScreenUpdateBehaviourType", defaultValue: "pause" },
|
offscreen: { propertyName: "offScreenUpdateBehaviour", type: "offScreenUpdateBehaviourType", defaultValue: "pause" },
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -876,7 +865,7 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
|
|||||||
* @returns The `HTMLElement` where the widget is hosted.
|
* @returns The `HTMLElement` where the widget is hosted.
|
||||||
*/
|
*/
|
||||||
public getHostElement (): HTMLElement {
|
public getHostElement (): HTMLElement {
|
||||||
return (this.width <= 0 || this.width <= 0) && !this.getAttribute("style")
|
return (this.width <= 0 || this.width <= 0) && !this.getAttribute("style") && !this.getAttribute("class")
|
||||||
? this.parentElement!
|
? this.parentElement!
|
||||||
: this;
|
: this;
|
||||||
}
|
}
|
||||||
@ -1009,18 +998,28 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
|
|||||||
// skeleton.scaleX = this.dprScale;
|
// skeleton.scaleX = this.dprScale;
|
||||||
// skeleton.scaleY = this.dprScale;
|
// skeleton.scaleY = this.dprScale;
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
|
||||||
// the bounds are calculated the first time, if no custom bound is provided
|
// the bounds are calculated the first time, if no custom bound is provided
|
||||||
this.initWidget(this.bounds.width <= 0 || this.bounds.height <= 0);
|
this.initWidget(this.bounds.width <= 0 || this.bounds.height <= 0);
|
||||||
|
|
||||||
this.loading = false;
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private initWidget (forceRecalculate = false) {
|
private initWidget (forceRecalculate = false) {
|
||||||
|
if (this.loading) return;
|
||||||
|
|
||||||
const { skeleton, state, animation, animations: animationsInfo, skin, defaultMix } = this;
|
const { skeleton, state, animation, animations: animationsInfo, skin, defaultMix } = this;
|
||||||
|
|
||||||
if (skin) {
|
if (skin) {
|
||||||
skeleton?.setSkinByName(skin);
|
if (skin.length === 1) {
|
||||||
|
skeleton?.setSkinByName(skin[0]);
|
||||||
|
} else {
|
||||||
|
const customSkin = new Skin("custom");
|
||||||
|
for (const s of skin) customSkin.addSkin(skeleton?.data.findSkin(s) as Skin);
|
||||||
|
skeleton?.setSkin(customSkin);
|
||||||
|
}
|
||||||
|
|
||||||
skeleton?.setSlotsToSetupPose();
|
skeleton?.setSlotsToSetupPose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1028,7 +1027,7 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
|
|||||||
state.data.defaultMix = defaultMix;
|
state.data.defaultMix = defaultMix;
|
||||||
|
|
||||||
if (animationsInfo) {
|
if (animationsInfo) {
|
||||||
for (const [trackIndexString, { cycle, animations }] of Object.entries(animationsInfo)) {
|
for (const [trackIndexString, { cycle, animations, repeatDelay }] of Object.entries(animationsInfo)) {
|
||||||
const cycleFn = () => {
|
const cycleFn = () => {
|
||||||
const trackIndex = Number(trackIndexString);
|
const trackIndex = Number(trackIndexString);
|
||||||
for (const [index, { animationName, delay, loop, mixDuration }] of animations.entries()) {
|
for (const [index, { animationName, delay, loop, mixDuration }] of animations.entries()) {
|
||||||
@ -1050,7 +1049,15 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
|
|||||||
if (mixDuration) track.mixDuration = mixDuration;
|
if (mixDuration) track.mixDuration = mixDuration;
|
||||||
|
|
||||||
if (cycle && index === animations.length - 1) {
|
if (cycle && index === animations.length - 1) {
|
||||||
track.listener = { complete: () => cycleFn() };
|
track.listener = {
|
||||||
|
complete: () => {
|
||||||
|
if (repeatDelay)
|
||||||
|
setTimeout(() => cycleFn(), 1000 * repeatDelay);
|
||||||
|
else
|
||||||
|
cycleFn();
|
||||||
|
delete track.listener?.complete;
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1068,22 +1075,13 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
private render (): void {
|
private render (): void {
|
||||||
let width;
|
let noSize = (!this.getAttribute("style") && !this.getAttribute("class"));
|
||||||
let height;
|
|
||||||
if (this.width === -1 || this.height === -1) {
|
|
||||||
width = "0";
|
|
||||||
height = "0";
|
|
||||||
} else {
|
|
||||||
width = `${this.width}px`
|
|
||||||
height = `${this.height}px`
|
|
||||||
}
|
|
||||||
this.root.innerHTML = `
|
this.root.innerHTML = `
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: ${width};
|
${noSize ? "width: 0; height: 0;" : ""}
|
||||||
height: ${height};
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
@ -1233,10 +1231,10 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
|
|||||||
* Other utilities
|
* Other utilities
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public boneFollowerList: Array<{ slot: Slot, bone: Bone, element: HTMLElement, followAttachmentAttach: boolean, followRotation: boolean, followOpacity: boolean, followScale: boolean, hideAttachment: boolean }> = [];
|
public boneFollowerList: Array<{ slot: Slot, bone: Bone, element: HTMLElement, followVisibility: boolean, followRotation: boolean, followOpacity: boolean, followScale: boolean, hideAttachment: boolean }> = [];
|
||||||
public followSlot (slotName: string | Slot, element: HTMLElement, options: { followAttachmentAttach?: boolean, followRotation?: boolean, followOpacity?: boolean, followScale?: boolean, hideAttachment?: boolean } = {}) {
|
public followSlot (slotName: string | Slot, element: HTMLElement, options: { followVisibility?: boolean, followRotation?: boolean, followOpacity?: boolean, followScale?: boolean, hideAttachment?: boolean } = {}) {
|
||||||
const {
|
const {
|
||||||
followAttachmentAttach = false,
|
followVisibility = false,
|
||||||
followRotation = true,
|
followRotation = true,
|
||||||
followOpacity = true,
|
followOpacity = true,
|
||||||
followScale = true,
|
followScale = true,
|
||||||
@ -1255,7 +1253,7 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
|
|||||||
element.style.left = '0px';
|
element.style.left = '0px';
|
||||||
element.style.display = 'none';
|
element.style.display = 'none';
|
||||||
|
|
||||||
this.boneFollowerList.push({ slot, bone: slot.bone, element, followAttachmentAttach, followRotation, followOpacity, followScale, hideAttachment });
|
this.boneFollowerList.push({ slot, bone: slot.bone, element, followVisibility, followRotation, followOpacity, followScale, hideAttachment });
|
||||||
this.overlay.addSlotFollowerElement(element);
|
this.overlay.addSlotFollowerElement(element);
|
||||||
}
|
}
|
||||||
public unfollowSlot (element: HTMLElement): HTMLElement | undefined {
|
public unfollowSlot (element: HTMLElement): HTMLElement | undefined {
|
||||||
|
|||||||
@ -27,10 +27,10 @@
|
|||||||
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import { AnimationsInfo, FitType, ModeType, OffScreenUpdateBehaviourType } from "./SpineWebComponentSkeleton.js";
|
import { AnimationsInfo, FitType, OffScreenUpdateBehaviourType } from "./SpineWebComponentSkeleton.js";
|
||||||
|
|
||||||
const animatonTypeRegExp = /\[([^\]]+)\]/g;
|
const animatonTypeRegExp = /\[([^\]]+)\]/g;
|
||||||
export type AttributeTypes = "string" | "number" | "boolean" | "array-number" | "array-string" | "object" | "fitType" | "modeType" | "offScreenUpdateBehaviourType" | "animationsInfo";
|
export type AttributeTypes = "string" | "number" | "boolean" | "array-number" | "array-string" | "object" | "fitType" | "offScreenUpdateBehaviourType" | "animationsInfo";
|
||||||
|
|
||||||
export function castValue (type: AttributeTypes, value: string | null, defaultValue?: any) {
|
export function castValue (type: AttributeTypes, value: string | null, defaultValue?: any) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -48,8 +48,6 @@ export function castValue (type: AttributeTypes, value: string | null, defaultVa
|
|||||||
return castObject(value, defaultValue);
|
return castObject(value, defaultValue);
|
||||||
case "fitType":
|
case "fitType":
|
||||||
return isFitType(value) ? value : defaultValue;
|
return isFitType(value) ? value : defaultValue;
|
||||||
case "modeType":
|
|
||||||
return isModeType(value) ? value : defaultValue;
|
|
||||||
case "offScreenUpdateBehaviourType":
|
case "offScreenUpdateBehaviourType":
|
||||||
return isOffScreenUpdateBehaviourType(value) ? value : defaultValue;
|
return isOffScreenUpdateBehaviourType(value) ? value : defaultValue;
|
||||||
case "animationsInfo":
|
case "animationsInfo":
|
||||||
@ -104,7 +102,7 @@ function castToAnimationsInfo (value: string | null): AnimationsInfo | undefined
|
|||||||
if (!matches) return undefined;
|
if (!matches) return undefined;
|
||||||
|
|
||||||
return matches.reduce((obj, group) => {
|
return matches.reduce((obj, group) => {
|
||||||
const [trackIndexStringOrLoopDefinition, animationNameOrTrackIndexStringCycle, loop, delayString, mixDurationString] = group.slice(1, -1).split(',').map(v => v.trim());
|
const [trackIndexStringOrLoopDefinition, animationNameOrTrackIndexStringCycle, loopOrRepeatDelay, delayString, mixDurationString] = group.slice(1, -1).split(',').map(v => v.trim());
|
||||||
|
|
||||||
if (trackIndexStringOrLoopDefinition === "loop") {
|
if (trackIndexStringOrLoopDefinition === "loop") {
|
||||||
if (!Number.isInteger(Number(animationNameOrTrackIndexStringCycle))) {
|
if (!Number.isInteger(Number(animationNameOrTrackIndexStringCycle))) {
|
||||||
@ -112,6 +110,15 @@ function castToAnimationsInfo (value: string | null): AnimationsInfo | undefined
|
|||||||
}
|
}
|
||||||
const animationInfoObject = obj[animationNameOrTrackIndexStringCycle] ||= { animations: [] };
|
const animationInfoObject = obj[animationNameOrTrackIndexStringCycle] ||= { animations: [] };
|
||||||
animationInfoObject.cycle = true;
|
animationInfoObject.cycle = true;
|
||||||
|
|
||||||
|
if (loopOrRepeatDelay !== undefined) {
|
||||||
|
const repeatDelay = Number(loopOrRepeatDelay);
|
||||||
|
if (Number.isNaN(repeatDelay)) {
|
||||||
|
throw new Error(`If present, duration of last animation of cycle in ${group} must be a positive integer number, instead it is ${loopOrRepeatDelay}. Original value: ${value}`);
|
||||||
|
}
|
||||||
|
animationInfoObject.repeatDelay = repeatDelay;
|
||||||
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,7 +146,7 @@ function castToAnimationsInfo (value: string | null): AnimationsInfo | undefined
|
|||||||
const animationInfoObject = obj[trackIndexStringOrLoopDefinition] ||= { animations: [] };
|
const animationInfoObject = obj[trackIndexStringOrLoopDefinition] ||= { animations: [] };
|
||||||
animationInfoObject.animations.push({
|
animationInfoObject.animations.push({
|
||||||
animationName: animationNameOrTrackIndexStringCycle,
|
animationName: animationNameOrTrackIndexStringCycle,
|
||||||
loop: loop.trim().toLowerCase() === "true",
|
loop: (loopOrRepeatDelay || "").trim().toLowerCase() === "true",
|
||||||
delay,
|
delay,
|
||||||
mixDuration,
|
mixDuration,
|
||||||
});
|
});
|
||||||
@ -155,7 +162,8 @@ function isFitType (value: string | null): value is FitType {
|
|||||||
value === "contain" ||
|
value === "contain" ||
|
||||||
value === "cover" ||
|
value === "cover" ||
|
||||||
value === "none" ||
|
value === "none" ||
|
||||||
value === "scaleDown"
|
value === "scaleDown" ||
|
||||||
|
value === "origin"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,12 +175,6 @@ function isOffScreenUpdateBehaviourType (value: string | null): value is OffScre
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isModeType (value: string | null): value is ModeType {
|
|
||||||
return (
|
|
||||||
value === "inside" ||
|
|
||||||
value === "origin"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const base64RegExp = /^(([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==))$/;
|
const base64RegExp = /^(([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==))$/;
|
||||||
export function isBase64 (str: string) {
|
export function isBase64 (str: string) {
|
||||||
return base64RegExp.test(str);
|
return base64RegExp.test(str);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@esotericsoftware/spine-webgl",
|
"name": "@esotericsoftware/spine-webgl",
|
||||||
"version": "4.2.81",
|
"version": "4.2.82",
|
||||||
"description": "The official Spine Runtimes for the web.",
|
"description": "The official Spine Runtimes for the web.",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
@ -31,6 +31,6 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@esotericsoftware/spine-core": "4.2.81"
|
"@esotericsoftware/spine-core": "4.2.82"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,6 +83,8 @@ namespace Spine.Unity.Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class SpineBuildEnvUtility {
|
public static class SpineBuildEnvUtility {
|
||||||
|
public const string SPINE_ALLOW_UNSAFE_CODE = "SPINE_ALLOW_UNSAFE";
|
||||||
|
|
||||||
static bool IsInvalidGroup (BuildTargetGroup group) {
|
static bool IsInvalidGroup (BuildTargetGroup group) {
|
||||||
int gi = (int)group;
|
int gi = (int)group;
|
||||||
return
|
return
|
||||||
@ -99,15 +101,18 @@ namespace Spine.Unity.Editor {
|
|||||||
if (IsInvalidGroup(group))
|
if (IsInvalidGroup(group))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
|
try {
|
||||||
if (!defines.Contains(define)) {
|
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
|
||||||
wasDefineAdded = true;
|
if (!defines.Contains(define)) {
|
||||||
if (defines.EndsWith(";", System.StringComparison.Ordinal))
|
wasDefineAdded = true;
|
||||||
defines += define;
|
if (defines.EndsWith(";", System.StringComparison.Ordinal))
|
||||||
else
|
defines += define;
|
||||||
defines += ";" + define;
|
else
|
||||||
|
defines += ";" + define;
|
||||||
|
|
||||||
PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines);
|
PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines);
|
||||||
|
}
|
||||||
|
} catch (System.Exception) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Debug.LogWarning("Please ignore errors \"PlayerSettings Validation: Requested build target group doesn't exist\" above");
|
Debug.LogWarning("Please ignore errors \"PlayerSettings Validation: Requested build target group doesn't exist\" above");
|
||||||
@ -127,15 +132,18 @@ namespace Spine.Unity.Editor {
|
|||||||
if (IsInvalidGroup(group))
|
if (IsInvalidGroup(group))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
|
try {
|
||||||
if (defines.Contains(define)) {
|
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
|
||||||
wasDefineRemoved = true;
|
if (defines.Contains(define)) {
|
||||||
if (defines.Contains(define + ";"))
|
wasDefineRemoved = true;
|
||||||
defines = defines.Replace(define + ";", "");
|
if (defines.Contains(define + ";"))
|
||||||
else
|
defines = defines.Replace(define + ";", "");
|
||||||
defines = defines.Replace(define, "");
|
else
|
||||||
|
defines = defines.Replace(define, "");
|
||||||
|
|
||||||
PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines);
|
PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines);
|
||||||
|
}
|
||||||
|
} catch (System.Exception) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -39,6 +39,14 @@
|
|||||||
#define HAS_ON_POSTPROCESS_PREFAB
|
#define HAS_ON_POSTPROCESS_PREFAB
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if UNITY_2021_2_OR_NEWER
|
||||||
|
#define TEXT_ASSET_HAS_GET_DATA_BYTES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if TEXT_ASSET_HAS_GET_DATA_BYTES
|
||||||
|
#define HAS_ANY_UNSAFE_OPTIONS
|
||||||
|
#endif
|
||||||
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@ -356,6 +364,18 @@ namespace Spine.Unity.Editor {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if HAS_ANY_UNSAFE_OPTIONS
|
||||||
|
GUILayout.Space(20);
|
||||||
|
EditorGUILayout.LabelField("Unsafe Build Defines", EditorStyles.boldLabel);
|
||||||
|
using (new GUILayout.HorizontalScope()) {
|
||||||
|
EditorGUILayout.PrefixLabel(new GUIContent("Direct data access", "Allow unsafe direct data access. Currently affects reading .skel.bytes files, reading with fewer allocations."));
|
||||||
|
if (GUILayout.Button("Enable", GUILayout.Width(64)))
|
||||||
|
SpineBuildEnvUtility.EnableBuildDefine(SpineBuildEnvUtility.SPINE_ALLOW_UNSAFE_CODE);
|
||||||
|
if (GUILayout.Button("Disable", GUILayout.Width(64)))
|
||||||
|
SpineBuildEnvUtility.DisableBuildDefine(SpineBuildEnvUtility.SPINE_ALLOW_UNSAFE_CODE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if SPINE_TK2D_DEFINE
|
#if SPINE_TK2D_DEFINE
|
||||||
bool isTK2DDefineSet = true;
|
bool isTK2DDefineSet = true;
|
||||||
#else
|
#else
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "spine-unity",
|
"name": "spine-unity",
|
||||||
"references": [ "spine-csharp" ]
|
"references": [ "spine-csharp" ],
|
||||||
|
"allowUnsafeCode": true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,14 +27,39 @@
|
|||||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
//#define SPINE_ALLOW_UNSAFE // note: this define can be set via Edit - Preferences - Spine.
|
||||||
|
|
||||||
|
#if UNITY_2021_2_OR_NEWER
|
||||||
|
#define TEXT_ASSET_HAS_GET_DATA_BYTES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SPINE_ALLOW_UNSAFE && TEXT_ASSET_HAS_GET_DATA_BYTES
|
||||||
|
#define UNSAFE_DIRECT_ACCESS_TEXT_ASSET_DATA
|
||||||
|
#endif
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
#if UNSAFE_DIRECT_ACCESS_TEXT_ASSET_DATA
|
||||||
|
using Unity.Collections;
|
||||||
|
#endif
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
using CompatibilityProblemInfo = Spine.Unity.SkeletonDataCompatibility.CompatibilityProblemInfo;
|
using CompatibilityProblemInfo = Spine.Unity.SkeletonDataCompatibility.CompatibilityProblemInfo;
|
||||||
|
|
||||||
namespace Spine.Unity {
|
namespace Spine.Unity {
|
||||||
|
#if UNSAFE_DIRECT_ACCESS_TEXT_ASSET_DATA
|
||||||
|
public static class TextAssetExtensions {
|
||||||
|
public static Stream GetStreamUnsafe (this TextAsset textAsset) {
|
||||||
|
NativeArray<byte> dataNativeArray = textAsset.GetData<byte>();
|
||||||
|
return dataNativeArray.GetUnmanagedMemoryStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unsafe UnmanagedMemoryStream GetUnmanagedMemoryStream<T> (this NativeArray<T> nativeArray) where T : struct {
|
||||||
|
return new UnmanagedMemoryStream((byte*)global::Unity.Collections.LowLevel.Unsafe.
|
||||||
|
NativeArrayUnsafeUtility.GetUnsafeReadOnlyPtr(nativeArray), nativeArray.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
[CreateAssetMenu(fileName = "New SkeletonDataAsset", menuName = "Spine/SkeletonData Asset")]
|
[CreateAssetMenu(fileName = "New SkeletonDataAsset", menuName = "Spine/SkeletonData Asset")]
|
||||||
public class SkeletonDataAsset : ScriptableObject {
|
public class SkeletonDataAsset : ScriptableObject {
|
||||||
@ -188,9 +213,15 @@ namespace Spine.Unity {
|
|||||||
SkeletonData loadedSkeletonData = null;
|
SkeletonData loadedSkeletonData = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (hasBinaryExtension)
|
if (hasBinaryExtension) {
|
||||||
|
#if UNSAFE_DIRECT_ACCESS_TEXT_ASSET_DATA
|
||||||
|
using (Stream stream = skeletonJSON.GetStreamUnsafe()) {
|
||||||
|
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(stream, attachmentLoader, skeletonDataScale);
|
||||||
|
}
|
||||||
|
#else
|
||||||
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.bytes, attachmentLoader, skeletonDataScale);
|
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.bytes, attachmentLoader, skeletonDataScale);
|
||||||
else
|
#endif
|
||||||
|
} else
|
||||||
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.text, attachmentLoader, skeletonDataScale);
|
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.text, attachmentLoader, skeletonDataScale);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
if (!quiet)
|
if (!quiet)
|
||||||
@ -287,6 +318,13 @@ namespace Spine.Unity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static SkeletonData ReadSkeletonData (Stream assetStream, AttachmentLoader attachmentLoader, float scale) {
|
||||||
|
SkeletonBinary binary = new SkeletonBinary(attachmentLoader) {
|
||||||
|
Scale = scale
|
||||||
|
};
|
||||||
|
return binary.ReadSkeletonData(assetStream);
|
||||||
|
}
|
||||||
|
|
||||||
internal static SkeletonData ReadSkeletonData (string text, AttachmentLoader attachmentLoader, float scale) {
|
internal static SkeletonData ReadSkeletonData (string text, AttachmentLoader attachmentLoader, float scale) {
|
||||||
StringReader input = new StringReader(text);
|
StringReader input = new StringReader(text);
|
||||||
SkeletonJson json = new SkeletonJson(attachmentLoader) {
|
SkeletonJson json = new SkeletonJson(attachmentLoader) {
|
||||||
|
|||||||
@ -27,6 +27,16 @@
|
|||||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
//#define SPINE_ALLOW_UNSAFE // note: this define can be set via Edit - Preferences - Spine.
|
||||||
|
|
||||||
|
#if UNITY_2021_2_OR_NEWER
|
||||||
|
#define TEXT_ASSET_HAS_GET_DATA_BYTES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SPINE_ALLOW_UNSAFE && TEXT_ASSET_HAS_GET_DATA_BYTES
|
||||||
|
#define UNSAFE_DIRECT_ACCESS_TEXT_ASSET_DATA
|
||||||
|
#endif
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@ -106,8 +116,12 @@ namespace Spine.Unity {
|
|||||||
|
|
||||||
if (fileVersion.sourceType == SourceType.Binary) {
|
if (fileVersion.sourceType == SourceType.Binary) {
|
||||||
try {
|
try {
|
||||||
using (MemoryStream memStream = new MemoryStream(asset.bytes)) {
|
#if UNSAFE_DIRECT_ACCESS_TEXT_ASSET_DATA
|
||||||
fileVersion.rawVersion = SkeletonBinary.GetVersionString(memStream);
|
using (Stream stream = asset.GetStreamUnsafe()) {
|
||||||
|
#else
|
||||||
|
using (MemoryStream stream = new MemoryStream(asset.bytes)) {
|
||||||
|
#endif
|
||||||
|
fileVersion.rawVersion = SkeletonBinary.GetVersionString(stream);
|
||||||
}
|
}
|
||||||
} catch (System.Exception e) {
|
} catch (System.Exception e) {
|
||||||
problemDescription = string.Format("Failed to read '{0}'. It is likely not a binary Spine SkeletonData file.\n{1}", asset.name, e);
|
problemDescription = string.Format("Failed to read '{0}'. It is likely not a binary Spine SkeletonData file.\n{1}", asset.name, e);
|
||||||
@ -162,8 +176,11 @@ namespace Spine.Unity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsJsonFile (TextAsset file) {
|
public static bool IsJsonFile (TextAsset file) {
|
||||||
|
#if TEXT_ASSET_HAS_GET_DATA_BYTES
|
||||||
|
var content = file.GetData<byte>();
|
||||||
|
#else
|
||||||
byte[] content = file.bytes;
|
byte[] content = file.bytes;
|
||||||
|
#endif
|
||||||
// check for binary skeleton version number string, starts after 8 byte hash
|
// check for binary skeleton version number string, starts after 8 byte hash
|
||||||
char majorVersionChar = compatibleBinaryVersions[0][0].ToString()[0];
|
char majorVersionChar = compatibleBinaryVersions[0][0].ToString()[0];
|
||||||
if (content.Length > 10 && content[9] == majorVersionChar && content[10] == '.')
|
if (content.Length > 10 && content[9] == majorVersionChar && content[10] == '.')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user