This commit is contained in:
Mario Zechner 2025-07-16 19:04:17 +02:00
commit 7656f4f7ef
223 changed files with 11723 additions and 15066 deletions

View File

@ -1,48 +1,28 @@
name: Check format
name: Check Formatting
on:
push:
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
check:
runs-on: ubuntu-24.04
steps:
- name: Install OS dependencies (needed for act on ubuntu-latest)
run: |
sudo apt update
sudo apt install -y --force-yes curl xz-utils libicu-dev git dos2unix
wget http://security.ubuntu.com/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb
sudo apt install ./libtinfo5_6.3-2ubuntu0.1_amd64.deb
- uses: actions/checkout@v3
- name: Cache Clang
id: cache-clang
uses: actions/cache@v3
with:
path: clang
key: ${{ runner.os }}-clang-13-0-1
- name: Install Clang
if: steps.cache-clang.outputs.cache-hit != 'true'
run: |
curl -L https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.1/clang+llvm-13.0.1-x86_64-linux-gnu-ubuntu-18.04.tar.xz --output clang.tar.xz
tar -xf clang.tar.xz
mv clang+llvm-13.0.1-x86_64-linux-gnu-ubuntu-18.04 clang
- name: Setup clang-format
run: ./formatters/setup-clang-format-docker.sh
- name: Install dotnet
uses: actions/setup-dotnet@v3
with:
dotnet-version: "6.0.x"
- run: dotnet tool install -g dotnet-format
dotnet-version: "8.0.x"
- name: Install Node and dependenceis
- name: Install Node and dependencies
uses: actions/setup-node@v3
with:
node-version: "16"
- run: npm install -g typescript typescript-formatter
node-version: "24"
- name: Install JDK
uses: actions/setup-java@v3
@ -50,14 +30,29 @@ jobs:
distribution: 'zulu'
java-version: "16"
- name: Install Haxe
uses: krdlab/setup-haxe@v1
with:
haxe-version: '4.3.2'
- run: haxelib install formatter
- name: Install Dart
uses: dart-lang/setup-dart@v1
with:
sdk: '3.8.1'
- name: Check dart format version
run: |
dart format --version
- name: Format
run: |
export CLANGFORMAT=`pwd`/clang/bin/clang-format
export PATH="$PATH:/root/.dotnet/tools"
dotnet-format --version
./formatters/format.sh
find . -type f -name '*.cs' -exec perl -pi -e 's/\r\n/\n/g' '{}' +
git diff
./formatters/format-cpp.sh
./formatters/format-csharp.sh
./formatters/format-dart.sh
./formatters/format-haxe.sh
./formatters/format-java.sh
./formatters/format-ts.sh
git diff > format-diff.txt
- name: Archive formatting result

2
.gitignore vendored
View File

@ -252,3 +252,5 @@ spine-libgdx/.project
.clang-format
spine-c/codegen/spine-cpp-types.json
spine-flutter/example/devtools_options.yaml
spine-glfw/.cache
formatters/eclipse-formatter/format-diff.txt

View File

@ -70,6 +70,7 @@ SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpaceInEmptyBlock: true
SpacesBeforeTrailingComments: 0
SpacesInAngles: false
SpacesInCStyleCastParentheses: false

View File

@ -12,7 +12,7 @@ indent_style = tab
tab_width = 4
# New line preferences
end_of_line = crlf
end_of_line = lf
insert_final_newline = true
#### .NET Coding Conventions ####

1
formatters/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
eclipse-formatter/target/

View File

@ -1,11 +1,103 @@
# Formatters
This folder contains formatter configuration files to be used with IDEs as well as the [spotless](https://github.com/diffplug/spotless/blob/main/plugin-gradle/README.md) formatter expressed in the Gradle project in this directory.
# Spine Runtimes Code Formatters
You will need the following on your `PATH`:
This directory contains formatting scripts and tools to ensure consistent code style across all Spine runtime implementations.
- JDK 10+
- clang-format 12.0.1 (i.e. `brew install clang-format`). Also set the environment variable `CLANGFORMAT` to the path of the `clang-format` executable.
- dotnet format (i.e. `dotnet tool install -g dotnet-format`, comes with dotnet 6 out of the box)
- tsfmt, (i.e. `npm install -g typescript-formatter`)
## Scripts
To run the formatter, invoke the `format.sh` script. This will shuffle around the Gradle config files, invoke spotless, then undo the config file shuffling. Invoking `./gradlew spotlessApply` from the `formatters/` directory will not work.
### format-cpp.sh
Formats C/C++ source files using clang-format.
- **Prerequisites**: clang-format 18.1.8
- **Coverage**: All C/C++ files in spine-cpp, spine-c, spine-godot, spine-ue, spine-glfw, spine-sdl, spine-sfml, spine-cocos2dx, spine-ios, spine-flutter
- **Configuration**: Uses .clang-format configuration file
- **Behavior**: Batches all files in a single clang-format call for performance
### format-csharp.sh
Formats C# source files using dotnet format.
- **Prerequisites**: .NET SDK with dotnet format tool
- **Coverage**: All C# files in spine-csharp, spine-monogame, spine-unity
- **Configuration**: Uses .editorconfig (temporarily copied to each project)
- **Behavior**: Runs with --no-restore and --verbosity quiet to suppress warnings
### format-dart.sh
Formats Dart source files using dart format.
- **Prerequisites**: Dart SDK
- **Coverage**: All Dart files in spine-flutter
- **Configuration**: Uses --page-width 120 parameter
- **Behavior**: Formats all files in place
### format-haxe.sh
Formats Haxe source files using haxelib formatter.
- **Prerequisites**: Haxe and haxelib formatter package
- **Coverage**: All Haxe files in spine-haxe
- **Configuration**: Uses hxformat.json configuration file
- **Behavior**: Formats all files in place
### format-java.sh
Formats Java source files using a custom Eclipse formatter.
- **Prerequisites**: Java JDK and Maven
- **Coverage**: All Java files in spine-libgdx and spine-android
- **Configuration**: Uses eclipse-formatter.xml configuration file
- **Behavior**: Builds Eclipse formatter JAR if needed, only outputs changed files
### format-ts.sh
Formats TypeScript source files using typescript-formatter.
- **Prerequisites**: Node.js and npm
- **Coverage**: All TypeScript files in spine-ts and tests
- **Configuration**: Uses tsfmt.json configuration files
- **Behavior**: Uses npx to auto-download formatter, validates tsfmt.json consistency
### setup-clang-format-docker.sh
Helper script for GitHub Actions to set up clang-format via Docker.
- **Prerequisites**: Docker
- **Purpose**: Creates a wrapper script that runs clang-format 18.1.8 in a Docker container
- **Usage**: Called by GitHub Actions workflow to ensure version consistency
## Configuration Files
- **.clang-format**: C/C++ formatting rules
- **.editorconfig**: C# formatting rules
- **eclipse-formatter.xml**: Java formatting rules for Eclipse formatter
- **hxformat.json**: Haxe formatting rules
## Eclipse Formatter
The eclipse-formatter directory contains a Maven project that builds a standalone Eclipse code formatter:
- **Source**: eclipse-formatter/src/main/java/com/esotericsoftware/spine/formatter/EclipseFormatter.java
- **Build**: Automatically built by format-java.sh when the JAR doesn't exist or source is newer
- **Output**: Only prints files that were actually modified
## GitHub Actions Workflow
The formatting check runs automatically on push and can be triggered manually:
1. **Workflow file**: .github/workflows/format-check-new.yml
2. **Process**:
- Sets up all required formatters and dependencies
- Runs all format scripts
- Captures any file changes to format-diff.txt
- Uploads diff as artifact
- Fails if any files were modified
### Docker-based clang-format
To ensure consistent formatting across local development and CI, the workflow uses Docker to run clang-format 18.1.8. The setup-clang-format-docker.sh script creates a wrapper that:
- Mounts the project directory in the Docker container
- Converts relative paths to absolute paths
- Runs clang-format with the same version everywhere
## Local Development
To run formatters locally:
```bash
cd formatters
./format-cpp.sh # Format C/C++ files
./format-csharp.sh # Format C# files
./format-dart.sh # Format Dart files
./format-haxe.sh # Format Haxe files
./format-java.sh # Format Java files
./format-ts.sh # Format TypeScript files
./format.sh # Format everything
```
Ensure you have the required tools installed for each formatter you want to run.

View File

@ -1,51 +0,0 @@
buildscript { repositories { mavenCentral() }}
plugins {
id "com.diffplug.spotless" version "5.14.0"
}
spotless {
lineEndings 'UNIX'
java {
target 'spine-libgdx/**/*.java',
'spine-android/**/*.java'
eclipse().configFile('formatters/eclipse-formatter.xml')
}
cpp {
target 'spine-c/**/*.c',
'spine-c/**/.h',
'spine-cpp/**/*.cpp',
'spine-cpp/**/.h',
'spine-cocos2dx/src/**/*.cpp',
'spine-cocos2dx/src/**/*.h',
'spine-cocos2dx/example/Classes/**/*.cpp',
'spine-cocos2dx/example/Classes/**/*.h',
'spine-glfw/src/**/*.cpp',
'spine-glfw/src/**/*.h',
'spine-glfw/example/**/*.cpp',
'spine-glfw/example/**/*.h',
'spine-sdl/src/**/*.c',
'spine-sdl/src/**/*.cpp',
'spine-sdl/src/**/*.h',
'spine-sdl/example/**/*.c',
'spine-sdl/example/**/*.cpp',
'spine-sdl/example/**/*.h',
'spine-sfml/c/src/**/*.c',
'spine-sfml/c/src/**/*.h',
'spine-sfml/cpp/src/**/*.cpp',
'spine-sfml/cpp/src/**/*.h',
'spine-ue/**/*.cpp',
'spine-ue/**/*.h',
'spine-godot/spine_godot/*.cpp',
'spine-godot/spine_godot/*.h'
clangFormat("13.0.1").pathToExe("$System.env.CLANGFORMAT").style('file')
}
typescript {
target 'spine-ts/**/src/*.ts'
targetExclude 'spine-ts/**/*.d.ts', 'spine-ts/**/node_modules/**/*.ts'
tsfmt('7.2.2').tsfmtFile('formatters/tsfmt.json')
}
}

View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.esotericsoftware.spine</groupId>
<artifactId>eclipse-formatter</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<eclipse.version>4.26</eclipse.version>
</properties>
<dependencies>
<!-- Eclipse JDT Core for formatting -->
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>org.eclipse.jdt.core</artifactId>
<version>3.32.0</version>
</dependency>
<!-- Eclipse Text framework -->
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.text</artifactId>
<version>3.12.300</version>
</dependency>
<!-- JFace Text -->
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.jface.text</artifactId>
<version>3.22.0</version>
</dependency>
<!-- Eclipse Core Runtime -->
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.core.runtime</artifactId>
<version>3.26.100</version>
</dependency>
<!-- Eclipse Equinox Common -->
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.equinox.common</artifactId>
<version>3.17.100</version>
</dependency>
<!-- Eclipse Core Resources -->
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.core.resources</artifactId>
<version>3.18.100</version>
</dependency>
<!-- Eclipse OSGi -->
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.osgi</artifactId>
<version>3.18.300</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
</plugin>
<!-- Create executable JAR with dependencies -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.esotericsoftware.spine.formatter.EclipseFormatter</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,129 @@
package com.esotericsoftware.spine.formatter;
import java.io.*;
import java.nio.file.*;
import java.util.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.eclipse.jdt.core.formatter.CodeFormatter;
import org.eclipse.jdt.internal.formatter.DefaultCodeFormatter;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.text.edits.TextEdit;
public class EclipseFormatter {
private static final String LINE_DELIMITER = "\n";
public static void main(String[] args) throws Exception {
if (args.length < 2) {
System.err.println("Usage: java -jar eclipse-formatter.jar <eclipse-formatter.xml> <file1.java> [file2.java ...]");
System.exit(1);
}
// Load formatter settings from XML
Map<String, String> options = loadFormatterSettings(args[0]);
// Create formatter
CodeFormatter formatter = new DefaultCodeFormatter(options);
// Format each file
int changedCount = 0;
int errorCount = 0;
for (int i = 1; i < args.length; i++) {
try {
if (formatFile(formatter, args[i])) {
changedCount++;
}
} catch (Exception e) {
System.err.println("Error formatting " + args[i] + ": " + e.getMessage());
errorCount++;
}
}
System.out.println("Formatting complete: " + changedCount + " files changed, " + errorCount + " errors");
if (errorCount > 0) {
System.exit(1);
}
}
private static Map<String, String> loadFormatterSettings(String xmlPath) throws Exception {
Map<String, String> settings = new HashMap<>();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
org.w3c.dom.Document doc = builder.parse(new File(xmlPath));
// Handle both direct settings and profile-based settings
NodeList profiles = doc.getElementsByTagName("profile");
if (profiles.getLength() > 0) {
// Profile-based format (Eclipse export)
Element profile = (Element) profiles.item(0);
NodeList settingNodes = profile.getElementsByTagName("setting");
for (int i = 0; i < settingNodes.getLength(); i++) {
Element setting = (Element) settingNodes.item(i);
String id = setting.getAttribute("id");
String value = setting.getAttribute("value");
if (id != null && !id.isEmpty() && value != null) {
settings.put(id, value);
}
}
} else {
// Direct settings format
NodeList settingNodes = doc.getElementsByTagName("setting");
for (int i = 0; i < settingNodes.getLength(); i++) {
Element setting = (Element) settingNodes.item(i);
String id = setting.getAttribute("id");
String value = setting.getAttribute("value");
if (id != null && !id.isEmpty() && value != null) {
settings.put(id, value);
}
}
}
// Removed verbose output
return settings;
}
private static boolean formatFile(CodeFormatter formatter, String filePath) throws Exception {
Path path = Paths.get(filePath);
if (!Files.exists(path)) {
throw new FileNotFoundException("File not found: " + filePath);
}
String content = Files.readString(path);
// Determine if it's a module-info.java file
int kind = path.getFileName().toString().equals("module-info.java")
? CodeFormatter.K_MODULE_INFO
: CodeFormatter.K_COMPILATION_UNIT;
kind |= CodeFormatter.F_INCLUDE_COMMENTS;
// Format the code
TextEdit edit = formatter.format(kind, content, 0, content.length(), 0, LINE_DELIMITER);
if (edit == null) {
throw new IllegalArgumentException("Cannot format file - invalid Java syntax or formatter configuration issue");
}
// Apply the edit
IDocument document = new Document(content);
edit.apply(document);
String formatted = document.get();
// Only write if content changed
if (!content.equals(formatted)) {
Files.writeString(path, formatted);
System.out.println("Formatted: " + filePath);
return true;
} else {
// Silent when no changes
return false;
}
}
}

View File

@ -6,8 +6,12 @@ echo "Formatting C/C++ files..."
dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
if [ ! -f "$dir/.clang-format" ]; then
# Store original directory
pushd "$dir" > /dev/null
if [ ! -f ".clang-format" ]; then
echo "Error: .clang-format not found in formatters directory"
popd > /dev/null
exit 1
fi
@ -84,28 +88,20 @@ done
echo "Found ${#files[@]} C/C++ files to format"
# Format each file with progress
count=0
errors=0
for file in "${files[@]}"; do
count=$((count + 1))
# Show progress every 10 files or for the last file
if [ $((count % 10)) -eq 0 ] || [ $count -eq ${#files[@]} ]; then
printf "\r[$count/${#files[@]}] Formatting: %-80s" "$(basename "$file")"
fi
# Format the file and capture any errors
if ! clang-format -i -style=file:"$dir/.clang-format" "$file" 2>/dev/null; then
printf "\nError formatting: $file\n"
errors=$((errors + 1))
fi
done
# Clear the progress line and show completion
printf "\r%-100s\r" " "
# Format all files in one call - works for both Docker and native
echo "Formatting ${#files[@]} files..."
if ! clang-format -i -style=file:".clang-format" "${files[@]}" 2>&1; then
echo "Error: clang-format failed"
errors=1
else
errors=0
fi
if [ $errors -gt 0 ]; then
echo "Completed with $errors errors"
fi
echo "C/C++ formatting complete"
echo "C/C++ formatting complete"
# Return to original directory
popd > /dev/null

View File

@ -6,20 +6,43 @@ echo "Formatting C# files..."
dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
if command -v dotnet-format &> /dev/null; then
if command -v dotnet &> /dev/null; then
# Store original directory
pushd "$dir" > /dev/null
# Copy .editorconfig to C# directories
cp .editorconfig ../spine-csharp/ 2>/dev/null || true
cp .editorconfig ../spine-monogame/ 2>/dev/null || true
cp .editorconfig ../spine-unity/ 2>/dev/null || true
dotnet-format ../spine-csharp/spine-csharp.sln || true
dotnet-format -f ../spine-monogame || true
dotnet-format -f ../spine-unity || true
# Format spine-csharp
pushd ../spine-csharp > /dev/null
dotnet format spine-csharp.csproj --no-restore --verbosity quiet 2>/dev/null || echo "Warning: Some issues with spine-csharp formatting"
popd > /dev/null
# Format spine-monogame
pushd ../spine-monogame > /dev/null
dotnet format --no-restore --verbosity quiet 2>/dev/null || echo "Warning: Some issues with spine-monogame formatting"
popd > /dev/null
# Format spine-unity - look for .cs files directly
if [ -d ../spine-unity ]; then
echo "Formatting spine-unity C# files directly..."
pushd ../spine-unity > /dev/null
# Find all .cs files and format them using dotnet format whitespace
find . -name "*.cs" -type f -not -path "./Library/*" -not -path "./Temp/*" -not -path "./obj/*" -not -path "./bin/*" | while read -r file; do
dotnet format whitespace --include "$file" --no-restore 2>/dev/null || true
done
popd > /dev/null
fi
# Clean up .editorconfig files
rm -f ../spine-csharp/.editorconfig
rm -f ../spine-monogame/.editorconfig
rm -f ../spine-unity/.editorconfig
# Return to original directory
popd > /dev/null
else
echo "Warning: dotnet-format not found. Skipping C# formatting."
echo "Warning: dotnet not found. Skipping C# formatting."
fi

View File

@ -4,12 +4,20 @@ set -e
# Format Dart files
echo "Formatting Dart files..."
dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
# Store original directory
pushd "$dir" > /dev/null
if command -v dart &> /dev/null; then
find .. -name "*.dart" \
-not -path "*/.*" \
-not -path "*/node_modules/*" \
-not -path "*/build/*" \
-exec dart format {} +
-exec dart format --page-width 120 {} +
else
echo "Warning: dart not found. Skipping Dart formatting."
fi
fi
# Return to original directory
popd > /dev/null

View File

@ -4,12 +4,19 @@ set -e
# Format Haxe files
echo "Formatting Haxe files..."
dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
# Store original directory
pushd "$dir" > /dev/null
if command -v haxelib &> /dev/null && haxelib list formatter &> /dev/null; then
find .. -name "*.hx" \
-not -path "*/.*" \
-not -path "*/node_modules/*" \
-not -path "*/build/*" \
| xargs haxelib run formatter -s
# Format spine-haxe directory
if [ -d ../spine-haxe ]; then
haxelib run formatter -s ../spine-haxe
fi
else
echo "Warning: haxe formatter not found. Install with: haxelib install formatter"
fi
fi
# Return to original directory
popd > /dev/null

View File

@ -1,9 +1,42 @@
#!/bin/bash
set -e
# Format Java files with Spotless (keeping this for Eclipse formatter)
# Format Java files with Eclipse formatter
echo "Formatting Java files..."
dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
./formatters/gradlew -p formatters spotlessJavaApply --quiet
# Store original directory
pushd "$dir" > /dev/null
# Build the Eclipse formatter if needed
jar_file="eclipse-formatter/target/eclipse-formatter-1.0.0-jar-with-dependencies.jar"
src_file="eclipse-formatter/src/main/java/com/esotericsoftware/spine/formatter/EclipseFormatter.java"
if [ ! -f "$jar_file" ] || [ "$src_file" -nt "$jar_file" ]; then
echo "Building Eclipse formatter..."
pushd eclipse-formatter > /dev/null
mvn -q clean package
popd > /dev/null
fi
# Find all Java files
java_files=$(find ../spine-libgdx ../spine-android -name "*.java" -type f \
-not -path "*/build/*" \
-not -path "*/.gradle/*" \
-not -path "*/bin/*" \
-not -path "*/gen/*" \
-not -path "*/target/*")
# Run the formatter
if [ -n "$java_files" ]; then
echo "Running Eclipse formatter on Java files..."
java -jar eclipse-formatter/target/eclipse-formatter-1.0.0-jar-with-dependencies.jar \
eclipse-formatter.xml \
$java_files
fi
echo "Java formatting complete"
# Return to original directory
popd > /dev/null

View File

@ -4,6 +4,11 @@ set -e
# Format Swift files
echo "Formatting Swift files..."
dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
# Store original directory
pushd "$dir" > /dev/null
if command -v swift-format &> /dev/null; then
find .. -name "*.swift" \
-not -path "*/.*" \
@ -12,4 +17,7 @@ if command -v swift-format &> /dev/null; then
| xargs swift-format -i
else
echo "Warning: swift-format not found. Install from https://github.com/apple/swift-format"
fi
fi
# Return to original directory
popd > /dev/null

View File

@ -6,13 +6,25 @@ echo "Formatting TypeScript files..."
dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
# Store original directory
pushd "$dir" > /dev/null
# Check if tsfmt.json files match
if ! cmp -s ../spine-ts/tsfmt.json ../tests/tsfmt.json; then
echo -e "\033[1;31mERROR: spine-ts/tsfmt.json and tests/tsfmt.json differ!\033[0m"
echo -e "\033[1;31mPlease sync them to ensure consistent formatting.\033[0m"
popd > /dev/null
exit 1
fi
# Format TypeScript files
cd ../spine-ts && npm run format && cd ../formatters
cd ../tests && npm run format -r && cd ../formatters
pushd ../spine-ts > /dev/null
npm run format
popd > /dev/null
pushd ../tests > /dev/null
npm run format
popd > /dev/null
# Return to original directory
popd > /dev/null

View File

@ -31,10 +31,10 @@ show_help() {
echo "Multiple language flags can be combined, e.g.: ./format.sh java ts"
echo ""
echo "Tools used:"
echo " Java: Spotless with Eclipse formatter"
echo " TypeScript: Biome"
echo " Java: Eclipse formatter (via eclipse-formatter.xml)"
echo " TypeScript: tsfmt (typescript-formatter)"
echo " C/C++: clang-format"
echo " C#: dotnet-format"
echo " C#: dotnet format"
echo " Haxe: haxe formatter"
echo " Dart: dart format"
echo " Swift: swift-format"

Binary file not shown.

View File

@ -1,5 +0,0 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip

172
formatters/gradlew vendored
View File

@ -1,172 +0,0 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

View File

@ -1,84 +0,0 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -1,3 +0,0 @@
include ':spine-libgdx:spine-libgdx'
include ':spine-libgdx:spine-libgdx-tests'
include ':spine-libgdx:spine-skeletonviewer'

View File

@ -0,0 +1,51 @@
#!/bin/bash
set -e
echo "Setting up clang-format Docker wrapper..."
# Pull the Docker image with clang-format 18
echo "Pulling Docker image..."
docker pull silkeh/clang:18
# Create a wrapper script that runs clang-format in Docker
echo "Creating wrapper script..."
cat > /tmp/clang-format-wrapper <<'EOF'
#!/bin/bash
# Get the absolute path of the file being formatted
args=()
for arg in "$@"; do
if [[ -f "$arg" ]]; then
# Convert to absolute path
args+=("$(realpath "$arg")")
else
args+=("$arg")
fi
done
# Find the project root (where .github directory is)
current_dir="$PWD"
while [[ "$current_dir" != "/" ]]; do
if [[ -d "$current_dir/.github" ]]; then
project_root="$current_dir"
break
fi
current_dir="$(dirname "$current_dir")"
done
# If we didn't find project root, use current directory's parent
if [[ -z "$project_root" ]]; then
project_root="$(dirname "$PWD")"
fi
# Run docker with the project root mounted
exec docker run --rm -i -v "$project_root:$project_root" -w "$PWD" silkeh/clang:18 clang-format "${args[@]}"
EOF
# Install the wrapper
sudo mv /tmp/clang-format-wrapper /usr/local/bin/clang-format
sudo chmod +x /usr/local/bin/clang-format
# Verify version and location
echo "Verifying installation..."
which clang-format
clang-format --version

View File

@ -32,7 +32,7 @@ fi
# Run codegen if requested
if [ "$1" = "codegen" ]; then
npx tsx codegen/src/index.ts
npx -y tsx codegen/src/index.ts
# Format the generated C++ files
echo "Formatting generated C++ files..."
../formatters/format.sh cpp

View File

@ -133,7 +133,7 @@ Generated files are output to `../src/generated/`:
# Install dependencies
npm install
npx tsx src/index.ts
npx -y tsx src/index.ts
# The generated files will be in ../src/generated/
```

View File

@ -1,113 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}</ProjectGuid>
<ProductVersion>9.0.21022</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Spine</RootNamespace>
<AssemblyName>spine-csharp</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<XnaFrameworkVersion>v4.0</XnaFrameworkVersion>
<XnaPlatform>Windows</XnaPlatform>
<XnaProfile>HiDef</XnaProfile>
<XnaCrossPlatformGroupID>99dfd52d-8beb-4e5c-a68b-365be39e8064</XnaCrossPlatformGroupID>
<XnaOutputType>Library</XnaOutputType>
<FileUpgradeFlags>
</FileUpgradeFlags>
<OldToolsVersion>3.5</OldToolsVersion>
<UpgradeBackupLocation />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;WINDOWS</DefineConstants>
<NoStdLib>true</NoStdLib>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;WINDOWS</DefineConstants>
<Optimize>true</Optimize>
<NoStdLib>true</NoStdLib>
<DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>
<ItemGroup>
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="System.Net" />
<Compile Include="src\**\*.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="src\Animation.cs" />
<Compile Include="src\AnimationState.cs" />
<Compile Include="src\AnimationStateData.cs" />
<Compile Include="src\Atlas.cs" />
<Compile Include="src\Attachments\AtlasAttachmentLoader.cs" />
<Compile Include="src\Attachments\Attachment.cs" />
<Compile Include="src\Attachments\AttachmentLoader.cs" />
<Compile Include="src\Attachments\AttachmentType.cs" />
<Compile Include="src\Attachments\BoundingBoxAttachment.cs" />
<Compile Include="src\Attachments\ClippingAttachment.cs" />
<Compile Include="src\Attachments\IHasTextureRegion.cs" />
<Compile Include="src\Attachments\MeshAttachment.cs" />
<Compile Include="src\Attachments\PathAttachment.cs" />
<Compile Include="src\Attachments\PointAttachment.cs" />
<Compile Include="src\Attachments\RegionAttachment.cs" />
<Compile Include="src\Attachments\Sequence.cs" />
<Compile Include="src\Attachments\VertexAttachment.cs" />
<Compile Include="src\BlendMode.cs" />
<Compile Include="src\Bone.cs" />
<Compile Include="src\BoneData.cs" />
<Compile Include="src\ConstraintData.cs" />
<Compile Include="src\PhysicsConstraint.cs" />
<Compile Include="src\PhysicsConstraintData.cs" />
<Compile Include="src\SkeletonLoader.cs" />
<Compile Include="src\TextureRegion.cs" />
<Compile Include="src\Triangulator.cs" />
<Compile Include="src\Event.cs" />
<Compile Include="src\EventData.cs" />
<Compile Include="src\ExposedList.cs" />
<Compile Include="src\IkConstraint.cs" />
<Compile Include="src\IkConstraintData.cs" />
<Compile Include="src\IUpdatable.cs" />
<Compile Include="src\Json.cs" />
<Compile Include="src\MathUtils.cs" />
<Compile Include="src\PathConstraint.cs" />
<Compile Include="src\PathConstraintData.cs" />
<Compile Include="src\Skeleton.cs" />
<Compile Include="src\SkeletonBinary.cs" />
<Compile Include="src\SkeletonBounds.cs" />
<Compile Include="src\SkeletonClipping.cs" />
<Compile Include="src\SkeletonData.cs" />
<Compile Include="src\SkeletonJson.cs" />
<Compile Include="src\Skin.cs" />
<Compile Include="src\Slot.cs" />
<Compile Include="src\SlotData.cs" />
<Compile Include="src\TransformConstraint.cs" />
<Compile Include="src\TransformConstraintData.cs" />
<None Remove="src\ColorUnity.cs.meta" />
<None Remove="src\ColorMono.cs.meta" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!--
To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -1,23 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spine-csharp", "spine-csharp.csproj", "{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
StartupItem = spine-csharp.csproj
EndGlobalSection
EndGlobal

View File

@ -51,7 +51,7 @@ namespace Spine {
}
public static implicit operator Color32F (Microsoft.Xna.Framework.Color xnaColor) {
return new Color32F(xnaColor);
return new Color32F(xnaColor);
}
public static implicit operator Microsoft.Xna.Framework.Color (Color32F c) {

View File

@ -36,7 +36,7 @@ namespace Spine {
internal int bendDirection;
internal bool compress, stretch;
internal float mix, softness;
public void Set (IkConstraintPose pose) {
mix = pose.mix;
softness = pose.softness;

View File

@ -36,24 +36,28 @@ class AnimatedLogin extends StatelessWidget {
@override
Widget build(BuildContext context) {
reportLeaks();
final controller = SpineWidgetController(onInitialized: (controller) {
controller.skeleton.setSkinByName("nate");
controller.skeleton.setToSetupPose();
controller.animationState.setAnimationByName(0, "login/look-left-down", true);
});
final controller = SpineWidgetController(
onInitialized: (controller) {
controller.skeleton.setSkinByName("nate");
controller.skeleton.setToSetupPose();
controller.animationState.setAnimationByName(0, "login/look-left-down", true);
},
);
return Scaffold(
appBar: AppBar(title: const Text('Animated login')),
body: Container(
margin: const EdgeInsets.all(15.0),
padding: const EdgeInsets.all(3.0),
decoration: BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: SpineWidget.fromAsset(
"assets/chibi/chibi-stickers.atlas",
"assets/chibi/chibi-stickers.skel",
controller,
boundsProvider: SkinAndAnimationBounds(skins: ["nate"], animation: "login/look-left-down"),
sizedByBounds: true,
)));
appBar: AppBar(title: const Text('Animated login')),
body: Container(
margin: const EdgeInsets.all(15.0),
padding: const EdgeInsets.all(3.0),
decoration: BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: SpineWidget.fromAsset(
"assets/chibi/chibi-stickers.atlas",
"assets/chibi/chibi-stickers.skel",
controller,
boundsProvider: SkinAndAnimationBounds(skins: ["nate"], animation: "login/look-left-down"),
sizedByBounds: true,
),
),
);
}
}

View File

@ -8,32 +8,38 @@ class AnimationStateEvents extends StatelessWidget {
@override
Widget build(BuildContext context) {
reportLeaks();
final controller = SpineWidgetController(onInitialized: (controller) {
controller.skeleton.setScaleX(0.5);
controller.skeleton.setScaleY(0.5);
controller.skeleton.findSlot("gun")?.setColor(Color(1, 0, 0, 1));
controller.animationStateData.setDefaultMix(0.2);
controller.animationState.setAnimationByName(0, "walk", true).setListener((type, trackEntry, event) {
print("Walk animation event $type");
});
controller.animationState.addAnimationByName(0, "jump", false, 2);
controller.animationState.addAnimationByName(0, "run", true, 0).setListener((type, trackEntry, event) {
print("Run animation event $type");
});
controller.animationState.setListener((type, trackEntry, event) {
if (type == EventType.event) {
print(
"User event: { name: ${event?.getData().getName()}, intValue: ${event?.getIntValue()}, floatValue: ${event?.getFloatValue()}, stringValue: ${event?.getStringValue()} }");
}
});
print("Current: ${controller.animationState.getCurrent(0)?.getAnimation().getName()}");
});
final controller = SpineWidgetController(
onInitialized: (controller) {
controller.skeleton.setScaleX(0.5);
controller.skeleton.setScaleY(0.5);
controller.skeleton.findSlot("gun")?.setColor(Color(1, 0, 0, 1));
controller.animationStateData.setDefaultMix(0.2);
controller.animationState.setAnimationByName(0, "walk", true).setListener((type, trackEntry, event) {
print("Walk animation event $type");
});
controller.animationState.addAnimationByName(0, "jump", false, 2);
controller.animationState.addAnimationByName(0, "run", true, 0).setListener((type, trackEntry, event) {
print("Run animation event $type");
});
controller.animationState.setListener((type, trackEntry, event) {
if (type == EventType.event) {
print(
"User event: { name: ${event?.getData().getName()}, intValue: ${event?.getIntValue()}, floatValue: ${event?.getFloatValue()}, stringValue: ${event?.getStringValue()} }",
);
}
});
print("Current: ${controller.animationState.getCurrent(0)?.getAnimation().getName()}");
},
);
return Scaffold(
appBar: AppBar(title: const Text('Animation State Listener')),
body: Column(children: [
appBar: AppBar(title: const Text('Animation State Listener')),
body: Column(
children: [
const Text("See output in console!"),
Expanded(child: SpineWidget.fromAsset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller))
]));
Expanded(child: SpineWidget.fromAsset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller)),
],
),
);
}
}

View File

@ -38,36 +38,40 @@ class DebugRendering extends StatelessWidget {
reportLeaks();
const debugRenderer = DebugRenderer();
final controller = SpineWidgetController(onInitialized: (controller) {
controller.animationState.setAnimationByName(0, "walk", true);
}, onBeforePaint: (controller, canvas) {
// Save the current transform and other canvas state
canvas.save();
final controller = SpineWidgetController(
onInitialized: (controller) {
controller.animationState.setAnimationByName(0, "walk", true);
},
onBeforePaint: (controller, canvas) {
// Save the current transform and other canvas state
canvas.save();
// Get the current canvas transform an invert it, so we can work in the
// canvas coordinate system.
final currentMatrix = canvas.getTransform();
final invertedMatrix = Matrix4.tryInvert(Matrix4.fromFloat64List(currentMatrix));
if (invertedMatrix != null) {
canvas.transform(invertedMatrix.storage);
}
// Get the current canvas transform an invert it, so we can work in the
// canvas coordinate system.
final currentMatrix = canvas.getTransform();
final invertedMatrix = Matrix4.tryInvert(Matrix4.fromFloat64List(currentMatrix));
if (invertedMatrix != null) {
canvas.transform(invertedMatrix.storage);
}
// Draw something.
final Paint paint = Paint()
..color = Colors.black
..strokeWidth = 2.0;
// Draw something.
final Paint paint = Paint()
..color = Colors.black
..strokeWidth = 2.0;
canvas.drawLine(
Offset(0, 0),
Offset(canvas.getLocalClipBounds().width, canvas.getLocalClipBounds().height),
paint,
);
canvas.drawLine(
Offset(0, 0),
Offset(canvas.getLocalClipBounds().width, canvas.getLocalClipBounds().height),
paint,
);
// Restore the old transform and canvas state
canvas.restore();
}, onAfterPaint: (controller, canvas, commands) {
debugRenderer.render(controller.drawable, canvas, commands);
});
// Restore the old transform and canvas state
canvas.restore();
},
onAfterPaint: (controller, canvas, commands) {
debugRenderer.render(controller.drawable, canvas, commands);
},
);
return Scaffold(
appBar: AppBar(title: const Text('Debug Renderer')),

View File

@ -84,23 +84,26 @@ class DressUpState extends State<DressUp> {
@override
Widget build(BuildContext context) {
final controller = SpineWidgetController(onInitialized: (controller) {
controller.animationState.setAnimationByName(0, "dance", true);
});
final controller = SpineWidgetController(
onInitialized: (controller) {
controller.animationState.setAnimationByName(0, "dance", true);
},
);
return Scaffold(
appBar: AppBar(title: const Text('Dress Up')),
body: _skinImages.isEmpty
? const SizedBox()
: Row(children: [
appBar: AppBar(title: const Text('Dress Up')),
body: _skinImages.isEmpty
? const SizedBox()
: Row(
children: [
SizedBox(
width: thumbnailSize,
child: ListView(
children: _skinImages.keys.map((skinName) {
var rawImageData = _skinImages[skinName]!;
var image = Image(image: RawImageProvider(rawImageData));
var box = SizedBox(width: 200, height: 200, child: image);
return GestureDetector(
children: _skinImages.keys.map((skinName) {
var rawImageData = _skinImages[skinName]!;
var image = Image(image: RawImageProvider(rawImageData));
var box = SizedBox(width: 200, height: 200, child: image);
return GestureDetector(
onTap: () {
_toggleSkin(skinName);
setState(() {});
@ -114,16 +117,22 @@ class DressUpState extends State<DressUp> {
color: Colors.grey,
backgroundBlendMode: painting.BlendMode.saturation,
),
child: box));
}).toList()),
child: box,
),
);
}).toList(),
),
),
Expanded(
child: SpineWidget.fromDrawable(
_drawable,
controller,
boundsProvider: SkinAndAnimationBounds(skins: ["full-skins/girl"]),
))
]));
child: SpineWidget.fromDrawable(
_drawable,
controller,
boundsProvider: SkinAndAnimationBounds(skins: ["full-skins/girl"]),
),
),
],
),
);
}
@override

View File

@ -50,8 +50,8 @@ class SpineComponent extends PositionComponent {
Anchor super.anchor = Anchor.topLeft,
super.children,
super.priority,
}) : _ownsDrawable = ownsDrawable,
_boundsProvider = boundsProvider {
}) : _ownsDrawable = ownsDrawable,
_boundsProvider = boundsProvider {
_drawable.update(0);
_bounds = _boundsProvider.computeBounds(_drawable);
size = Vector2(_bounds.width, _bounds.height);
@ -69,15 +69,17 @@ class SpineComponent extends PositionComponent {
Iterable<Component>? children,
int? priority,
}) async {
return SpineComponent(await SkeletonDrawable.fromAsset(atlasFile, skeletonFile, bundle: bundle),
ownsDrawable: true,
boundsProvider: boundsProvider,
position: position,
scale: scale,
angle: angle,
anchor: anchor,
children: children,
priority: priority);
return SpineComponent(
await SkeletonDrawable.fromAsset(atlasFile, skeletonFile, bundle: bundle),
ownsDrawable: true,
boundsProvider: boundsProvider,
position: position,
scale: scale,
angle: angle,
anchor: anchor,
children: children,
priority: priority,
);
}
void dispose() {
@ -114,8 +116,13 @@ class SimpleFlameExample extends FlameGame {
// Load the Spineboy atlas and skeleton data from asset files
// and create a SpineComponent from them, scaled down and
// centered on the screen
spineboy = await SpineComponent.fromAssets("assets/spineboy.atlas", "assets/spineboy-pro.json",
scale: Vector2(0.4, 0.4), anchor: Anchor.center, position: Vector2(size.x / 2, size.y / 2));
spineboy = await SpineComponent.fromAssets(
"assets/spineboy.atlas",
"assets/spineboy-pro.json",
scale: Vector2(0.4, 0.4),
anchor: Anchor.center,
position: Vector2(size.x / 2, size.y / 2),
);
// Set the "walk" animation on track 0 in looping mode
spineboy.animationState.setAnimationByName(0, "walk", true);
@ -137,7 +144,7 @@ class DragonExample extends FlameGame {
@override
Future<void> onLoad() async {
cachedAtlas = await Atlas.fromAsset("assets/dragon.atlas");
cachedSkeletonData = await SkeletonData.fromAsset(cachedAtlas, "assets/dragon-ess.skel");
cachedSkeletonData = await SkeletonData.fromAsset(cachedAtlas, "assets/dragon-ess.skel");
final drawable = SkeletonDrawable(cachedAtlas, cachedSkeletonData, false);
dragon = SpineComponent(
drawable,
@ -205,6 +212,9 @@ class SpineFlameGameWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: const Text('Flame Integration')), body: GameWidget(game: game));
return Scaffold(
appBar: AppBar(title: const Text('Flame Integration')),
body: GameWidget(game: game),
);
}
}

View File

@ -45,19 +45,22 @@ class IkFollowingState extends State<IkFollowing> {
void initState() {
super.initState();
controller = SpineWidgetController(onInitialized: (controller) {
// Set the walk animation on track 0, let it loop
controller.animationState.setAnimationByName(0, "walk", true);
controller.animationState.setAnimationByName(1, "aim", true);
}, onAfterUpdateWorldTransforms: (controller) {
final worldPosition = crossHairPosition;
if (worldPosition == null) return;
final bone = controller.skeleton.findBone("crosshair")!;
final parent = bone.getParent()!;
final position = parent.worldToLocal(worldPosition.dx, worldPosition.dy);
bone.setX(position.x);
bone.setY(position.y);
});
controller = SpineWidgetController(
onInitialized: (controller) {
// Set the walk animation on track 0, let it loop
controller.animationState.setAnimationByName(0, "walk", true);
controller.animationState.setAnimationByName(1, "aim", true);
},
onAfterUpdateWorldTransforms: (controller) {
final worldPosition = crossHairPosition;
if (worldPosition == null) return;
final bone = controller.skeleton.findBone("crosshair")!;
final parent = bone.getParent()!;
final position = parent.worldToLocal(worldPosition.dx, worldPosition.dy);
bone.setX(position.x);
bone.setY(position.y);
},
);
}
void _updateBonePosition(Offset position) {
@ -69,11 +72,17 @@ class IkFollowingState extends State<IkFollowing> {
reportLeaks();
return Scaffold(
appBar: AppBar(title: const Text('IK Following')),
body: GestureDetector(
onPanDown: (drag) => _updateBonePosition(drag.localPosition),
onPanUpdate: (drag) => _updateBonePosition(drag.localPosition),
child: SpineWidget.fromAsset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller, alignment: Alignment.centerLeft,),
));
appBar: AppBar(title: const Text('IK Following')),
body: GestureDetector(
onPanDown: (drag) => _updateBonePosition(drag.localPosition),
onPanUpdate: (drag) => _updateBonePosition(drag.localPosition),
child: SpineWidget.fromAsset(
"assets/spineboy.atlas",
"assets/spineboy-pro.skel",
controller,
alignment: Alignment.centerLeft,
),
),
);
}
}

View File

@ -46,130 +46,96 @@ class ExampleSelector extends StatelessWidget {
Widget build(BuildContext context) {
const spacer = SizedBox(height: 10);
return Scaffold(
appBar: AppBar(title: const Text('Spine Examples')),
body: Center(
child: Column(mainAxisSize: MainAxisSize.min, children: [
ElevatedButton(
child: const Text('Simple Animation'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => const SimpleAnimation(),
),
);
},
),
spacer,
ElevatedButton(
child: const Text('Pause/Play animation'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => const PlayPauseAnimation(),
),
);
},
),
spacer,
ElevatedButton(
child: const Text('Animation State Listener'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => const AnimationStateEvents(),
),
);
},
),
spacer,
ElevatedButton(
child: const Text('Debug Rendering'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => const DebugRendering(),
),
);
},
),
spacer,
ElevatedButton(
child: const Text('Dress Up'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => const DressUp(),
),
);
},
),
spacer,
ElevatedButton(
child: const Text('IK Following'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => const IkFollowing(),
),
);
},
),
spacer,
ElevatedButton(
child: const Text('Physics'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => const PhysicsTest(),
),
);
},
),
spacer,
ElevatedButton(
child: const Text('Flame: Simple Example'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => SpineFlameGameWidget(SimpleFlameExample()),
),
);
},
),
spacer,
ElevatedButton(
child: const Text('Flame: Pre-load and share Spine data'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => SpineFlameGameWidget(PreloadAndShareSpineDataExample()),
),
);
},
),
spacer,
ElevatedButton(
child: const Text('Flame: Dragon Example'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => SpineFlameGameWidget(DragonExample()),
),
);
},
),
spacer,
])));
appBar: AppBar(title: const Text('Spine Examples')),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton(
child: const Text('Simple Animation'),
onPressed: () {
Navigator.push(context, MaterialPageRoute<void>(builder: (context) => const SimpleAnimation()));
},
),
spacer,
ElevatedButton(
child: const Text('Pause/Play animation'),
onPressed: () {
Navigator.push(context, MaterialPageRoute<void>(builder: (context) => const PlayPauseAnimation()));
},
),
spacer,
ElevatedButton(
child: const Text('Animation State Listener'),
onPressed: () {
Navigator.push(context, MaterialPageRoute<void>(builder: (context) => const AnimationStateEvents()));
},
),
spacer,
ElevatedButton(
child: const Text('Debug Rendering'),
onPressed: () {
Navigator.push(context, MaterialPageRoute<void>(builder: (context) => const DebugRendering()));
},
),
spacer,
ElevatedButton(
child: const Text('Dress Up'),
onPressed: () {
Navigator.push(context, MaterialPageRoute<void>(builder: (context) => const DressUp()));
},
),
spacer,
ElevatedButton(
child: const Text('IK Following'),
onPressed: () {
Navigator.push(context, MaterialPageRoute<void>(builder: (context) => const IkFollowing()));
},
),
spacer,
ElevatedButton(
child: const Text('Physics'),
onPressed: () {
Navigator.push(context, MaterialPageRoute<void>(builder: (context) => const PhysicsTest()));
},
),
spacer,
ElevatedButton(
child: const Text('Flame: Simple Example'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(builder: (context) => SpineFlameGameWidget(SimpleFlameExample())),
);
},
),
spacer,
ElevatedButton(
child: const Text('Flame: Pre-load and share Spine data'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => SpineFlameGameWidget(PreloadAndShareSpineDataExample()),
),
);
},
),
spacer,
ElevatedButton(
child: const Text('Flame: Dragon Example'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(builder: (context) => SpineFlameGameWidget(DragonExample())),
);
},
),
spacer,
],
),
),
);
}
}

View File

@ -43,9 +43,11 @@ class PlayPauseAnimationState extends State<PlayPauseAnimation> {
@override
void initState() {
super.initState();
controller = SpineWidgetController(onInitialized: (controller) {
controller.animationState.setAnimationByName(0, "flying", true);
});
controller = SpineWidgetController(
onInitialized: (controller) {
controller.animationState.setAnimationByName(0, "flying", true);
},
);
}
void _togglePlay() {

View File

@ -46,26 +46,29 @@ class PhysicsState extends State<PhysicsTest> {
void initState() {
super.initState();
controller = SpineWidgetController(onInitialized: (controller) {
controller.animationState.setAnimationByName(0, "eyeblink-long", true);
controller.animationState.setAnimationByName(1, "wings-and-feet", true);
}, onAfterUpdateWorldTransforms: (controller) {
if (lastMousePosition == null) {
lastMousePosition = mousePosition;
return;
}
if (mousePosition == null) {
return;
}
controller = SpineWidgetController(
onInitialized: (controller) {
controller.animationState.setAnimationByName(0, "eyeblink-long", true);
controller.animationState.setAnimationByName(1, "wings-and-feet", true);
},
onAfterUpdateWorldTransforms: (controller) {
if (lastMousePosition == null) {
lastMousePosition = mousePosition;
return;
}
if (mousePosition == null) {
return;
}
final dx = mousePosition!.dx - lastMousePosition!.dx;
final dy = mousePosition!.dy - lastMousePosition!.dy;
final position = controller.skeleton.getPosition();
position.x += dx;
position.y += dy;
controller.skeleton.setPosition(position.x, position.y);
lastMousePosition = mousePosition;
});
final dx = mousePosition!.dx - lastMousePosition!.dx;
final dy = mousePosition!.dy - lastMousePosition!.dy;
final position = controller.skeleton.getPosition();
position.x += dx;
position.y += dy;
controller.skeleton.setPosition(position.x, position.y);
lastMousePosition = mousePosition;
},
);
}
void _updateBonePosition(Offset position) {
@ -77,11 +80,12 @@ class PhysicsState extends State<PhysicsTest> {
reportLeaks();
return Scaffold(
appBar: AppBar(title: const Text('Physics (drag anywhere)')),
body: GestureDetector(
onPanDown: (drag) => _updateBonePosition(drag.localPosition),
onPanUpdate: (drag) => _updateBonePosition(drag.localPosition),
child: SpineWidget.fromAsset("assets/celestial-circus.atlas", "assets/celestial-circus-pro.skel", controller),
));
appBar: AppBar(title: const Text('Physics (drag anywhere)')),
body: GestureDetector(
onPanDown: (drag) => _updateBonePosition(drag.localPosition),
onPanUpdate: (drag) => _updateBonePosition(drag.localPosition),
child: SpineWidget.fromAsset("assets/celestial-circus.atlas", "assets/celestial-circus-pro.skel", controller),
),
);
}
}

View File

@ -36,18 +36,20 @@ class SimpleAnimation extends StatelessWidget {
@override
Widget build(BuildContext context) {
reportLeaks();
final controller = SpineWidgetController(onInitialized: (controller) {
// Set the default mixing time between animations
controller.animationState.getData().setDefaultMix(0.2);
// Set the portal animation on track 0
controller.animationState.setAnimationByName(0, "portal", true);
// Queue the run animation after the portal animation
controller.animationState.addAnimationByName(0, "run", true, 0);
});
final controller = SpineWidgetController(
onInitialized: (controller) {
// Set the default mixing time between animations
controller.animationState.getData().setDefaultMix(0.2);
// Set the portal animation on track 0
controller.animationState.setAnimationByName(0, "portal", true);
// Queue the run animation after the portal animation
controller.animationState.addAnimationByName(0, "run", true, 0);
},
);
return Scaffold(
appBar: AppBar(title: const Text('Simple Animation')),
body: SpineWidget.fromAsset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller)
body: SpineWidget.fromAsset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller),
);
}
}

View File

@ -91,8 +91,9 @@ Future<SpineFlutterFFI> initSpineFlutterFFI(bool useStaticLinkage) async {
registerOpaqueType<spine_skin_entries_wrapper>();
await js.importLibrary('assets/packages/spine_flutter/lib/assets/libspine_flutter.js');
Uint8List wasmBinaries =
(await rootBundle.load('packages/spine_flutter/lib/assets/libspine_flutter.wasm')).buffer.asUint8List();
Uint8List wasmBinaries = (await rootBundle.load(
'packages/spine_flutter/lib/assets/libspine_flutter.wasm',
)).buffer.asUint8List();
_module = await EmscriptenModule.compile(wasmBinaries, 'libspine_flutter');
}
Module? m = _module;

View File

@ -32,12 +32,7 @@ class RawImageProvider extends ImageProvider<_RawImageKey> {
final double? scale;
final int? targetWidth;
final int? targetHeight;
RawImageProvider(
this.image, {
this.scale = 1.0,
this.targetWidth,
this.targetHeight,
});
RawImageProvider(this.image, {this.scale = 1.0, this.targetWidth, this.targetHeight});
@override
ImageStreamCompleter loadImage(_RawImageKey key, ImageDecoderCallback decode) {
@ -69,8 +64,7 @@ class RawImageProvider extends ImageProvider<_RawImageKey> {
debugPrint('ImageDescriptor: ${descriptor.width}x${descriptor.height}');
return true;
}());
return descriptor.instantiateCodec(
targetWidth: targetWidth, targetHeight: targetHeight);
return descriptor.instantiateCodec(targetWidth: targetWidth, targetHeight: targetHeight);
}
}
@ -105,16 +99,10 @@ class RawImageData {
final int height;
final ui.PixelFormat pixelFormat;
RawImageData(
this.pixels,
this.width,
this.height, {
this.pixelFormat = ui.PixelFormat.rgba8888,
});
RawImageData(this.pixels, this.width, this.height, {this.pixelFormat = ui.PixelFormat.rgba8888});
_RawImageKey? _key;
_RawImageKey _obtainKey() {
return _key ??=
_RawImageKey(width, height, pixelFormat.index, md5.convert(pixels));
return _key ??= _RawImageKey(width, height, pixelFormat.index, md5.convert(pixels));
}
}

View File

@ -39,7 +39,6 @@ import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import 'package:path/path.dart' as path;
import 'ffi_proxy.dart';
import 'init.dart' if (dart.library.html) 'init_web.dart';
import 'spine_flutter_bindings_generated.dart';
@ -137,9 +136,13 @@ class Atlas {
Map<BlendMode, Paint> paints = {};
for (final blendMode in BlendMode.values) {
paints[blendMode] = Paint()
..shader = ImageShader(image, TileMode.clamp, TileMode.clamp, Matrix4
.identity()
.storage, filterQuality: Atlas.filterQuality)
..shader = ImageShader(
image,
TileMode.clamp,
TileMode.clamp,
Matrix4.identity().storage,
filterQuality: Atlas.filterQuality,
)
..isAntiAlias = true
..blendMode = blendMode.canvasBlendMode;
}
@ -747,8 +750,12 @@ class BoneData {
/// rendered at runtime.
Color getColor() {
final color = _bindings.spine_bone_data_get_color(_data);
return Color(_bindings.spine_color_get_r(color), _bindings.spine_color_get_g(color), _bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color));
return Color(
_bindings.spine_color_get_r(color),
_bindings.spine_color_get_g(color),
_bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color),
);
}
void setColor(double r, double g, double b, double a) {
@ -797,7 +804,15 @@ class Bone {
///
/// See [World transform](http://esotericsoftware.com/spine-runtime-skeletons#World-transforms) in the Spine
/// Runtimes Guide.
void updateWorldTransformWith(double x, double y, double rotation, double scaleX, double scaleY, double shearX, double shearY) {
void updateWorldTransformWith(
double x,
double y,
double rotation,
double scaleX,
double scaleY,
double shearX,
double shearY,
) {
_bindings.spine_bone_update_world_transform_with(_bone, x, y, rotation, scaleX, scaleY, shearX, shearY);
}
@ -1123,8 +1138,12 @@ class SlotData {
/// color tinting.
Color getColor() {
final color = _bindings.spine_slot_data_get_color(_data);
return Color(_bindings.spine_color_get_r(color), _bindings.spine_color_get_g(color), _bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color));
return Color(
_bindings.spine_color_get_r(color),
_bindings.spine_color_get_g(color),
_bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color),
);
}
void setColor(double r, double g, double b, double a) {
@ -1135,8 +1154,12 @@ class SlotData {
/// color's alpha is not used.
Color getDarkColor() {
final color = _bindings.spine_slot_data_get_dark_color(_data);
return Color(_bindings.spine_color_get_r(color), _bindings.spine_color_get_g(color), _bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color));
return Color(
_bindings.spine_color_get_r(color),
_bindings.spine_color_get_g(color),
_bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color),
);
}
void setDarkColor(double r, double g, double b, double a) {
@ -1211,8 +1234,12 @@ class Slot {
/// color tinting.
Color getColor() {
final color = _bindings.spine_slot_get_color(_slot);
return Color(_bindings.spine_color_get_r(color), _bindings.spine_color_get_g(color), _bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color));
return Color(
_bindings.spine_color_get_r(color),
_bindings.spine_color_get_g(color),
_bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color),
);
}
void setColor(Color color) {
@ -1223,8 +1250,12 @@ class Slot {
/// color's alpha is not used.
Color getDarkColor() {
final color = _bindings.spine_slot_get_dark_color(_slot);
return Color(_bindings.spine_color_get_r(color), _bindings.spine_color_get_g(color), _bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color));
return Color(
_bindings.spine_color_get_r(color),
_bindings.spine_color_get_g(color),
_bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color),
);
}
void setDarkColor(Color color) {
@ -1574,8 +1605,12 @@ class RegionAttachment extends Attachment<spine_region_attachment> {
Color getColor() {
final color = _bindings.spine_region_attachment_get_color(_attachment);
return Color(_bindings.spine_color_get_r(color), _bindings.spine_color_get_g(color), _bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color));
return Color(
_bindings.spine_color_get_r(color),
_bindings.spine_color_get_g(color),
_bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color),
);
}
void setColor(double r, double g, double b, double a) {
@ -1662,7 +1697,9 @@ class VertexAttachment<T extends Pointer> extends Attachment<T> {
void setTimelineAttachment(Attachment? attachment) {
_bindings.spine_vertex_attachment_set_timeline_attachment(
_attachment.cast(), attachment == null ? nullptr : attachment._attachment.cast());
_attachment.cast(),
attachment == null ? nullptr : attachment._attachment.cast(),
);
}
}
@ -1713,8 +1750,12 @@ class MeshAttachment extends VertexAttachment<spine_mesh_attachment> {
Color getColor() {
final color = _bindings.spine_mesh_attachment_get_color(_attachment);
return Color(_bindings.spine_color_get_r(color), _bindings.spine_color_get_g(color), _bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color));
return Color(
_bindings.spine_color_get_r(color),
_bindings.spine_color_get_g(color),
_bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color),
);
}
void setColor(double r, double g, double b, double a) {
@ -1798,8 +1839,12 @@ class ClippingAttachment extends VertexAttachment<spine_clipping_attachment> {
/// attachments are not usually rendered at runtime.
Color getColor() {
final color = _bindings.spine_clipping_attachment_get_color(_attachment);
return Color(_bindings.spine_color_get_r(color), _bindings.spine_color_get_g(color), _bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color));
return Color(
_bindings.spine_color_get_r(color),
_bindings.spine_color_get_g(color),
_bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color),
);
}
void setColor(double r, double g, double b, double a) {
@ -1819,8 +1864,12 @@ class BoundingBoxAttachment extends VertexAttachment<spine_bounding_box_attachme
/// are not usually rendered at runtime.
Color getColor() {
final color = _bindings.spine_bounding_box_attachment_get_color(_attachment);
return Color(_bindings.spine_color_get_r(color), _bindings.spine_color_get_g(color), _bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color));
return Color(
_bindings.spine_color_get_r(color),
_bindings.spine_color_get_g(color),
_bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color),
);
}
void setColor(double r, double g, double b, double a) {
@ -1864,8 +1913,12 @@ class PathAttachment extends VertexAttachment<spine_path_attachment> {
/// rendered at runtime.
Color getColor() {
final color = _bindings.spine_path_attachment_get_color(_attachment);
return Color(_bindings.spine_color_get_r(color), _bindings.spine_color_get_g(color), _bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color));
return Color(
_bindings.spine_color_get_r(color),
_bindings.spine_color_get_g(color),
_bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color),
);
}
void setColor(double r, double g, double b, double a) {
@ -1919,8 +1972,12 @@ class PointAttachment extends Attachment<spine_point_attachment> {
/// attachments are not usually rendered at runtime.
Color getColor() {
final color = _bindings.spine_point_attachment_get_color(_attachment);
return Color(_bindings.spine_color_get_r(color), _bindings.spine_color_get_g(color), _bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color));
return Color(
_bindings.spine_color_get_r(color),
_bindings.spine_color_get_g(color),
_bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color),
);
}
void setColor(double r, double g, double b, double a) {
@ -1968,7 +2025,12 @@ class Skin {
/// Adds an attachment to the skin for the specified slot index and name.
void setAttachment(int slotIndex, String name, Attachment? attachment) {
final nativeName = name.toNativeUtf8(allocator: _allocator);
_bindings.spine_skin_set_attachment(_skin, slotIndex, nativeName.cast(), attachment == null ? nullptr : attachment._attachment.cast());
_bindings.spine_skin_set_attachment(
_skin,
slotIndex,
nativeName.cast(),
attachment == null ? nullptr : attachment._attachment.cast(),
);
_allocator.free(nativeName);
}
@ -2007,12 +2069,15 @@ class Skin {
for (int i = 0; i < numEntries; i++) {
final entry = _bindings.spine_skin_entries_get_entry(entries, i);
Pointer<Utf8> name = _bindings.spine_skin_entry_get_name(entry).cast();
result.add(SkinEntry(
result.add(
SkinEntry(
_bindings.spine_skin_entry_get_slot_index(entry),
name.toDartString(),
_bindings.spine_skin_entry_get_attachment(entry).address == nullptr.address
? null
: Attachment._toSubclass(_bindings.spine_skin_entry_get_attachment(entry))));
: Attachment._toSubclass(_bindings.spine_skin_entry_get_attachment(entry)),
),
);
}
return result;
}
@ -2836,7 +2901,11 @@ class Skeleton {
Attachment? getAttachmentByName(String slotName, String attachmentName) {
final slotNameNative = slotName.toNativeUtf8(allocator: _allocator);
final attachmentNameNative = attachmentName.toNativeUtf8(allocator: _allocator);
final attachment = _bindings.spine_skeleton_get_attachment_by_name(_skeleton, slotNameNative.cast(), attachmentNameNative.cast());
final attachment = _bindings.spine_skeleton_get_attachment_by_name(
_skeleton,
slotNameNative.cast(),
attachmentNameNative.cast(),
);
_allocator.free(slotNameNative);
_allocator.free(attachmentNameNative);
if (attachment.address == nullptr.address) return null;
@ -2898,8 +2967,12 @@ class Skeleton {
/// Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose.
Bounds getBounds() {
final nativeBounds = _bindings.spine_skeleton_get_bounds(_skeleton);
final bounds = Bounds(_bindings.spine_bounds_get_x(nativeBounds), _bindings.spine_bounds_get_y(nativeBounds),
_bindings.spine_bounds_get_width(nativeBounds), _bindings.spine_bounds_get_height(nativeBounds));
final bounds = Bounds(
_bindings.spine_bounds_get_x(nativeBounds),
_bindings.spine_bounds_get_y(nativeBounds),
_bindings.spine_bounds_get_width(nativeBounds),
_bindings.spine_bounds_get_height(nativeBounds),
);
return bounds;
}
@ -2993,8 +3066,12 @@ class Skeleton {
/// The color to tint all the skeleton's attachments.
Color getColor() {
final color = _bindings.spine_skeleton_get_color(_skeleton);
return Color(_bindings.spine_color_get_r(color), _bindings.spine_color_get_g(color), _bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color));
return Color(
_bindings.spine_color_get_r(color),
_bindings.spine_color_get_g(color),
_bindings.spine_color_get_b(color),
_bindings.spine_color_get_a(color),
);
}
void setColor(Color color) {
@ -3508,7 +3585,7 @@ enum EventType {
///
/// Because this event is triggered at the end of [AnimationState.apply], any animations set in response to
/// the event won't be applied until the next time the [AnimationState] is applied.
event
event,
}
/// Stores the setup pose values for an [Event].
@ -3801,7 +3878,12 @@ class AnimationState {
/// See [setAnimation].
TrackEntry setAnimationByName(int trackIndex, String animationName, bool loop) {
final animation = animationName.toNativeUtf8(allocator: _allocator);
final entry = _bindings.spine_animation_state_set_animation_by_name(_state, trackIndex, animation.cast(), loop ? -1 : 0);
final entry = _bindings.spine_animation_state_set_animation_by_name(
_state,
trackIndex,
animation.cast(),
loop ? -1 : 0,
);
_allocator.free(animation);
if (entry.address == nullptr.address) throw Exception("Couldn't set animation $animationName");
return TrackEntry._(entry, this);
@ -3816,7 +3898,12 @@ class AnimationState {
/// Returns a track entry to allow further customization of animation playback. References to the track entry must not be kept
/// after the [EventType.dispose] event occurs.
TrackEntry setAnimation(int trackIndex, Animation animation, bool loop) {
final entry = _bindings.spine_animation_state_set_animation(_state, trackIndex, animation._animation, loop ? -1 : 0);
final entry = _bindings.spine_animation_state_set_animation(
_state,
trackIndex,
animation._animation,
loop ? -1 : 0,
);
if (entry.address == nullptr.address) throw Exception("Couldn't set animation ${animation.getName()}");
return TrackEntry._(entry, this);
}
@ -3826,7 +3913,13 @@ class AnimationState {
/// See [addAnimation].
TrackEntry addAnimationByName(int trackIndex, String animationName, bool loop, double delay) {
final animation = animationName.toNativeUtf8(allocator: _allocator);
final entry = _bindings.spine_animation_state_add_animation_by_name(_state, trackIndex, animation.cast(), loop ? -1 : 0, delay);
final entry = _bindings.spine_animation_state_add_animation_by_name(
_state,
trackIndex,
animation.cast(),
loop ? -1 : 0,
delay,
);
_allocator.free(animation);
if (entry.address == nullptr.address) throw Exception("Couldn't add animation $animationName");
return TrackEntry._(entry, this);
@ -3843,7 +3936,13 @@ class AnimationState {
/// Returns a track entry to allow further customization of animation playback. References to the track entry must not be kept
/// after the [EventType.dispose] event occurs.
TrackEntry addAnimation(int trackIndex, Animation animation, bool loop, double delay) {
final entry = _bindings.spine_animation_state_add_animation(_state, trackIndex, animation._animation, loop ? -1 : 0, delay);
final entry = _bindings.spine_animation_state_add_animation(
_state,
trackIndex,
animation._animation,
loop ? -1 : 0,
delay,
);
if (entry.address == nullptr.address) throw Exception("Couldn't add animation ${animation.getName()}");
return TrackEntry._(entry, this);
}
@ -3966,8 +4065,10 @@ class SkeletonDrawable {
_drawable = _bindings.spine_skeleton_drawable_create(skeletonData._data);
skeleton = Skeleton._(_bindings.spine_skeleton_drawable_get_skeleton(_drawable));
animationStateData = AnimationStateData._(_bindings.spine_skeleton_drawable_get_animation_state_data(_drawable));
animationState = AnimationState._(_bindings.spine_skeleton_drawable_get_animation_state(_drawable),
_bindings.spine_skeleton_drawable_get_animation_state_events(_drawable));
animationState = AnimationState._(
_bindings.spine_skeleton_drawable_get_animation_state(_drawable),
_bindings.spine_skeleton_drawable_get_animation_state_events(_drawable),
);
skeleton.updateWorldTransform(Physics.none);
}
@ -4030,7 +4131,11 @@ class SkeletonDrawable {
List<RenderCommand> renderToCanvas(Canvas canvas) {
var commands = render();
for (final cmd in commands) {
canvas.drawVertices(cmd.vertices, rendering.BlendMode.modulate, atlas.atlasPagePaints[cmd.atlasPageIndex][cmd.blendMode]!);
canvas.drawVertices(
cmd.vertices,
rendering.BlendMode.modulate,
atlas.atlasPagePaints[cmd.atlasPageIndex][cmd.blendMode]!,
);
}
return commands;
}
@ -4070,10 +4175,10 @@ class SkeletonDrawable {
/// Scales and centers the skeleton to fit the within the bounds of [width] and [height].
Future<RawImageData> renderToRawImageData(double width, double height, int bgColor) async {
final recorder = renderToPictureRecorder(width, height, bgColor);
var rawImageData =
(await (await recorder.endRecording().toImage(width.toInt(), height.toInt())).toByteData(format: ImageByteFormat.rawRgba))!
.buffer
.asUint8List();
var rawImageData = (await (await recorder.endRecording().toImage(
width.toInt(),
height.toInt(),
)).toByteData(format: ImageByteFormat.rawRgba))!.buffer.asUint8List();
return RawImageData(rawImageData, width.toInt(), height.toInt());
}
@ -4135,7 +4240,13 @@ class RenderCommand {
if (colors.isNotEmpty && colors[0] == -1) {
vertices = Vertices.raw(VertexMode.triangles, positions, textureCoordinates: uvs, indices: indices);
} else {
vertices = Vertices.raw(VertexMode.triangles, positions, textureCoordinates: uvs, colors: colors, indices: indices);
vertices = Vertices.raw(
VertexMode.triangles,
positions,
textureCoordinates: uvs,
colors: colors,
indices: indices,
);
}
} else {
// On the web, rendering is done through CanvasKit, which requires copies of the native data.
@ -4143,7 +4254,13 @@ class RenderCommand {
final uvsCopy = Float32List.fromList(uvs);
final colorsCopy = Int32List.fromList(colors);
final indicesCopy = Uint16List.fromList(indices);
vertices = Vertices.raw(VertexMode.triangles, positionsCopy, textureCoordinates: uvsCopy, colors: colorsCopy, indices: indicesCopy);
vertices = Vertices.raw(
VertexMode.triangles,
positionsCopy,
textureCoordinates: uvsCopy,
colors: colorsCopy,
indices: indicesCopy,
);
}
}
}
@ -4158,7 +4275,10 @@ class DebugRenderer {
..color = material.Colors.blue
..style = PaintingStyle.fill;
for (final bone in drawable.skeleton.getBones()) {
canvas.drawRect(Rect.fromCenter(center: Offset(bone.getWorldX(), bone.getWorldY()), width: 5, height: 5), bonePaint);
canvas.drawRect(
Rect.fromCenter(center: Offset(bone.getWorldX(), bone.getWorldY()), width: 5, height: 5),
bonePaint,
);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -74,8 +74,13 @@ class SpineWidgetController {
/// Constructs a new [SpineWidget] controller. See the class documentation of [SpineWidgetController] for information on
/// the optional arguments.
SpineWidgetController(
{this.onInitialized, this.onBeforeUpdateWorldTransforms, this.onAfterUpdateWorldTransforms, this.onBeforePaint, this.onAfterPaint});
SpineWidgetController({
this.onInitialized,
this.onBeforeUpdateWorldTransforms,
this.onAfterUpdateWorldTransforms,
this.onBeforePaint,
this.onAfterPaint,
});
void _initialize(SkeletonDrawable drawable) {
var wasInitialized = _drawable != null;
@ -203,7 +208,7 @@ class SkinAndAnimationBounds extends BoundsProvider {
/// The [stepTime], given in seconds, defines at what interval the bounds should be sampled
/// across the entire animation.
SkinAndAnimationBounds({List<String>? skins, this.animation, this.stepTime = 0.1})
: skins = skins == null || skins.isEmpty ? ["default"] : skins;
: skins = skins == null || skins.isEmpty ? ["default"] : skins;
@override
Bounds computeBounds(SkeletonDrawable drawable) {
@ -282,15 +287,23 @@ class SpineWidget extends StatefulWidget {
/// are used.
///
/// The widget can optionally by sized by the bounds provided by the [BoundsProvider] by passing `true` for [sizedByBounds].
SpineWidget.fromAsset(this._atlasFile, this._skeletonFile, this._controller,
{AssetBundle? bundle, BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
: _assetType = _AssetType.asset,
_fit = fit ?? BoxFit.contain,
_alignment = alignment ?? Alignment.center,
_boundsProvider = boundsProvider ?? const SetupPoseBounds(),
_sizedByBounds = sizedByBounds ?? false,
_drawable = null,
_bundle = bundle ?? rootBundle;
SpineWidget.fromAsset(
this._atlasFile,
this._skeletonFile,
this._controller, {
AssetBundle? bundle,
BoxFit? fit,
Alignment? alignment,
BoundsProvider? boundsProvider,
bool? sizedByBounds,
super.key,
}) : _assetType = _AssetType.asset,
_fit = fit ?? BoxFit.contain,
_alignment = alignment ?? Alignment.center,
_boundsProvider = boundsProvider ?? const SetupPoseBounds(),
_sizedByBounds = sizedByBounds ?? false,
_drawable = null,
_bundle = bundle ?? rootBundle;
/// Constructs a new [SpineWidget] from files. The [_atlasFile] specifies the `.atlas` file to be loaded for the images used to render
/// the skeleton. The [_skeletonFile] specifies either a Skeleton `.json` or `.skel` file containing the skeleton data.
@ -303,15 +316,22 @@ class SpineWidget extends StatefulWidget {
/// are used.
///
/// The widget can optionally by sized by the bounds provided by the [BoundsProvider] by passing `true` for [sizedByBounds].
const SpineWidget.fromFile(this._atlasFile, this._skeletonFile, this._controller,
{BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
: _assetType = _AssetType.file,
_bundle = null,
_fit = fit ?? BoxFit.contain,
_alignment = alignment ?? Alignment.center,
_boundsProvider = boundsProvider ?? const SetupPoseBounds(),
_sizedByBounds = sizedByBounds ?? false,
_drawable = null;
const SpineWidget.fromFile(
this._atlasFile,
this._skeletonFile,
this._controller, {
BoxFit? fit,
Alignment? alignment,
BoundsProvider? boundsProvider,
bool? sizedByBounds,
super.key,
}) : _assetType = _AssetType.file,
_bundle = null,
_fit = fit ?? BoxFit.contain,
_alignment = alignment ?? Alignment.center,
_boundsProvider = boundsProvider ?? const SetupPoseBounds(),
_sizedByBounds = sizedByBounds ?? false,
_drawable = null;
/// Constructs a new [SpineWidget] from HTTP URLs. The [_atlasFile] specifies the `.atlas` file to be loaded for the images used to render
/// the skeleton. The [_skeletonFile] specifies either a Skeleton `.json` or `.skel` file containing the skeleton data.
@ -324,15 +344,22 @@ class SpineWidget extends StatefulWidget {
/// are used.
///
/// The widget can optionally by sized by the bounds provided by the [BoundsProvider] by passing `true` for [sizedByBounds].
const SpineWidget.fromHttp(this._atlasFile, this._skeletonFile, this._controller,
{BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
: _assetType = _AssetType.http,
_bundle = null,
_fit = fit ?? BoxFit.contain,
_alignment = alignment ?? Alignment.center,
_boundsProvider = boundsProvider ?? const SetupPoseBounds(),
_sizedByBounds = sizedByBounds ?? false,
_drawable = null;
const SpineWidget.fromHttp(
this._atlasFile,
this._skeletonFile,
this._controller, {
BoxFit? fit,
Alignment? alignment,
BoundsProvider? boundsProvider,
bool? sizedByBounds,
super.key,
}) : _assetType = _AssetType.http,
_bundle = null,
_fit = fit ?? BoxFit.contain,
_alignment = alignment ?? Alignment.center,
_boundsProvider = boundsProvider ?? const SetupPoseBounds(),
_sizedByBounds = sizedByBounds ?? false,
_drawable = null;
/// Constructs a new [SpineWidget] from a [SkeletonDrawable].
///
@ -344,16 +371,22 @@ class SpineWidget extends StatefulWidget {
/// are used.
///
/// The widget can optionally by sized by the bounds provided by the [BoundsProvider] by passing `true` for [sizedByBounds].
const SpineWidget.fromDrawable(this._drawable, this._controller,
{BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
: _assetType = _AssetType.drawable,
_bundle = null,
_fit = fit ?? BoxFit.contain,
_alignment = alignment ?? Alignment.center,
_boundsProvider = boundsProvider ?? const SetupPoseBounds(),
_sizedByBounds = sizedByBounds ?? false,
_skeletonFile = null,
_atlasFile = null;
const SpineWidget.fromDrawable(
this._drawable,
this._controller, {
BoxFit? fit,
Alignment? alignment,
BoundsProvider? boundsProvider,
bool? sizedByBounds,
super.key,
}) : _assetType = _AssetType.drawable,
_bundle = null,
_fit = fit ?? BoxFit.contain,
_alignment = alignment ?? Alignment.center,
_boundsProvider = boundsProvider ?? const SetupPoseBounds(),
_sizedByBounds = sizedByBounds ?? false,
_skeletonFile = null,
_atlasFile = null;
@override
State<SpineWidget> createState() => _SpineWidgetState();
@ -382,8 +415,7 @@ class _SpineWidgetState extends State<SpineWidget> {
// loaded.
bool hasChanged = true;
if (oldWidget._assetType == widget._assetType) {
if (oldWidget._assetType == _AssetType.drawable &&
oldWidget._drawable == widget._drawable) {
if (oldWidget._assetType == _AssetType.drawable && oldWidget._drawable == widget._drawable) {
hasChanged = false;
} else if (oldWidget._skeletonFile == widget._skeletonFile &&
oldWidget._atlasFile == widget._atlasFile &&
@ -431,7 +463,13 @@ class _SpineWidgetState extends State<SpineWidget> {
Widget build(BuildContext context) {
if (_drawable != null) {
return _SpineRenderObjectWidget(
_drawable!, widget._controller, widget._fit, widget._alignment, _computedBounds, widget._sizedByBounds);
_drawable!,
widget._controller,
widget._fit,
widget._alignment,
_computedBounds,
widget._sizedByBounds,
);
} else {
return const SizedBox();
}
@ -452,7 +490,14 @@ class _SpineRenderObjectWidget extends LeafRenderObjectWidget {
final Bounds _bounds;
final bool _sizedByBounds;
const _SpineRenderObjectWidget(this._skeletonDrawable, this._controller, this._fit, this._alignment, this._bounds, this._sizedByBounds);
const _SpineRenderObjectWidget(
this._skeletonDrawable,
this._controller,
this._fit,
this._alignment,
this._bounds,
this._sizedByBounds,
);
@override
RenderObject createRenderObject(BuildContext context) {
@ -481,7 +526,14 @@ class _SpineRenderObject extends RenderBox {
bool _disposed = false;
bool _firstUpdated = false;
_SpineRenderObject(this._skeletonDrawable, this._controller, this._fit, this._alignment, this._bounds, this._sizedByBounds);
_SpineRenderObject(
this._skeletonDrawable,
this._controller,
this._fit,
this._alignment,
this._bounds,
this._sizedByBounds,
);
set skeletonDrawable(SkeletonDrawable skeletonDrawable) {
if (_skeletonDrawable == skeletonDrawable) return;

View File

@ -0,0 +1,41 @@
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 20,
"patch": 0
},
"configurePresets": [
{
"name": "debug",
"displayName": "Debug",
"description": "Debug build with compile commands",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
},
{
"name": "release",
"displayName": "Release",
"description": "Release build",
"inherits": "debug",
"binaryDir": "${sourceDir}/build/release",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
}
],
"buildPresets": [
{
"name": "debug",
"configurePreset": "debug"
},
{
"name": "release",
"configurePreset": "release"
}
]
}

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package;
@ -35,63 +35,61 @@ import starlingExamples.Scene.SceneManager;
import starling.core.Starling;
import flixel.FlxG;
import flixel.FlxGame;
import openfl.display.Sprite;
import openfl.text.TextField;
import openfl.text.TextFormat;
import openfl.events.MouseEvent;
import openfl.geom.Rectangle;
import starling.events.Event;
class Main extends Sprite {
private var background:Sprite;
private var background:Sprite;
private var flixelButton:Sprite;
private var starlingButton:Sprite;
private var starlingButton:Sprite;
private var uiContainer:Sprite;
private static inline var ratio = 4;
private static inline var STAGE_WIDTH:Int = 100 * ratio;
private static inline var STAGE_HEIGHT:Int = 200 * ratio;
private static inline var BUTTON_WIDTH:Int = 80 * ratio;
private static inline var BUTTON_HEIGHT:Int = 40 * ratio;
private static inline var BUTTON_SPACING:Int = 20 * ratio;
private static inline var STAGE_WIDTH:Int = 100 * ratio;
private static inline var STAGE_HEIGHT:Int = 200 * ratio;
private static inline var BUTTON_WIDTH:Int = 80 * ratio;
private static inline var BUTTON_HEIGHT:Int = 40 * ratio;
private static inline var BUTTON_SPACING:Int = 20 * ratio;
public function new() {
super();
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
public function new() {
super();
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
private function onAddedToStage(e:Event):Void {
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
createUI();
centerUI();
stage.addEventListener(Event.RESIZE, onResize);
}
private function onAddedToStage(e:Event):Void {
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
createUI();
centerUI();
stage.addEventListener(Event.RESIZE, onResize);
}
private function createUI():Void {
uiContainer = new Sprite();
addChild(uiContainer);
private function createUI():Void {
uiContainer = new Sprite();
addChild(uiContainer);
background = new Sprite();
background.graphics.beginFill(0xA2A2A2);
background.graphics.drawRect(0, 0, STAGE_WIDTH, STAGE_HEIGHT);
background.graphics.endFill();
uiContainer.addChild(background);
background = new Sprite();
background.graphics.beginFill(0xA2A2A2);
background.graphics.drawRect(0, 0, STAGE_WIDTH, STAGE_HEIGHT);
background.graphics.endFill();
uiContainer.addChild(background);
flixelButton = createButton("Flixel", 0xFF0000);
uiContainer.addChild(flixelButton);
flixelButton = createButton("Flixel", 0xFF0000);
uiContainer.addChild(flixelButton);
starlingButton = createButton("Starling", 0x00FF00);
uiContainer.addChild(starlingButton);
starlingButton = createButton("Starling", 0x00FF00);
uiContainer.addChild(starlingButton);
positionButtons();
positionButtons();
flixelButton.addEventListener(MouseEvent.CLICK, onFlixelClick);
starlingButton.addEventListener(MouseEvent.CLICK, onStarlingClick);
}
flixelButton.addEventListener(MouseEvent.CLICK, onFlixelClick);
starlingButton.addEventListener(MouseEvent.CLICK, onStarlingClick);
}
private function createButton(label:String, color:Int):Sprite {
private function createButton(label:String, color:Int):Sprite {
var button = new Sprite();
var g = button.graphics;
@ -116,53 +114,54 @@ class Main extends Sprite {
return button;
}
private function positionButtons():Void {
var totalHeight = (BUTTON_HEIGHT * 2) + BUTTON_SPACING;
var startY = (STAGE_HEIGHT - totalHeight) / 2;
private function positionButtons():Void {
var totalHeight = (BUTTON_HEIGHT * 2) + BUTTON_SPACING;
var startY = (STAGE_HEIGHT - totalHeight) / 2;
flixelButton.x = (STAGE_WIDTH - BUTTON_WIDTH) / 2;
flixelButton.y = startY + BUTTON_HEIGHT + BUTTON_SPACING;
flixelButton.x = (STAGE_WIDTH - BUTTON_WIDTH) / 2;
flixelButton.y = startY + BUTTON_HEIGHT + BUTTON_SPACING;
starlingButton.x = (STAGE_WIDTH - BUTTON_WIDTH) / 2;
starlingButton.y = startY;
}
starlingButton.x = (STAGE_WIDTH - BUTTON_WIDTH) / 2;
starlingButton.y = startY;
}
private function centerUI():Void {
uiContainer.x = (stage.stageWidth - STAGE_WIDTH) / 2;
uiContainer.y = (stage.stageHeight - STAGE_HEIGHT) / 2;
}
uiContainer.x = (stage.stageWidth - STAGE_WIDTH) / 2;
uiContainer.y = (stage.stageHeight - STAGE_HEIGHT) / 2;
}
private function onResize(e:Event):Void {
centerUI();
}
private function onResize(e:Event):Void {
centerUI();
}
private function onFlixelClick(e:MouseEvent):Void {
trace("Launching Flixel game");
private function onFlixelClick(e:MouseEvent):Void {
trace("Launching Flixel game");
destroyUI();
addChild(new FlxGame(640, 480, FlixelState));
FlxG.autoPause = false;
}
}
private function destroyUI():Void {
flixelButton.removeEventListener(MouseEvent.CLICK, onFlixelClick);
starlingButton.removeEventListener(MouseEvent.CLICK, onStarlingClick);
stage.removeEventListener(Event.RESIZE, onResize);
flixelButton.removeEventListener(MouseEvent.CLICK, onFlixelClick);
starlingButton.removeEventListener(MouseEvent.CLICK, onStarlingClick);
stage.removeEventListener(Event.RESIZE, onResize);
removeChild(uiContainer);
removeChild(uiContainer);
background = null;
flixelButton = null;
starlingButton = null;
uiContainer = null;
}
background = null;
flixelButton = null;
starlingButton = null;
uiContainer = null;
}
private var starlingSingleton:Starling;
private function onStarlingClick(e:MouseEvent):Void {
trace("Launching Starling game");
private function onStarlingClick(e:MouseEvent):Void {
trace("Launching Starling game");
starlingSingleton = new Starling(starling.display.Sprite, stage, new Rectangle(0, 0, 800, 600));
starlingSingleton.supportHighResolutions = true;
starlingSingleton.addEventListener(Event.ROOT_CREATED, onStarlingRootCreated);
}
}
private function onStarlingRootCreated(event:Event):Void {
destroyUI();

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package;
@ -34,10 +34,8 @@ import flixel.FlxG;
import flixel.FlxGame;
import openfl.display.Sprite;
class MainFlixel extends Sprite
{
public function new()
{
class MainFlixel extends Sprite {
public function new() {
super();
addChild(new FlxGame(640, 480, FlixelState));
FlxG.autoPause = false;

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package;

View File

@ -25,11 +25,10 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package flixelExamples;
import flixel.util.FlxColor;
import flixel.text.FlxText;
import spine.Skin;

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package flixelExamples;
@ -43,6 +43,7 @@ class BasicExample extends FlxState {
var loadBinary = true;
var skeletonSprite:SkeletonSprite;
override public function create():Void {
FlxG.cameras.bgColor = 0xffa1b2b0;
@ -66,22 +67,20 @@ class BasicExample extends FlxState {
trace("loaded");
}
override public function update(elapsed:Float):Void
{
if (FlxG.keys.anyPressed([RIGHT])) {
skeletonSprite.x += 15;
}
if (FlxG.keys.anyPressed([LEFT])) {
skeletonSprite.x -= 15;
}
if (FlxG.keys.anyPressed([DOWN])) {
skeletonSprite.y += 15;
}
if (FlxG.keys.anyPressed([UP])) {
skeletonSprite.y -= 15;
}
super.update(elapsed);
override public function update(elapsed:Float):Void {
if (FlxG.keys.anyPressed([RIGHT])) {
skeletonSprite.x += 15;
}
if (FlxG.keys.anyPressed([LEFT])) {
skeletonSprite.x -= 15;
}
if (FlxG.keys.anyPressed([DOWN])) {
skeletonSprite.y += 15;
}
if (FlxG.keys.anyPressed([UP])) {
skeletonSprite.y -= 15;
}
super.update(elapsed);
}
}

View File

@ -25,11 +25,10 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package flixelExamples;
import flixel.text.FlxText;
import flixel.math.FlxPoint;
import spine.Skin;
@ -47,6 +46,7 @@ class CelestialCircusExample extends FlxState {
var loadBinary = true;
var skeletonSprite:SkeletonSprite;
override public function create():Void {
FlxG.cameras.bgColor = 0xffa1b2b0;
@ -55,7 +55,8 @@ class CelestialCircusExample extends FlxState {
add(button);
var atlas = new TextureAtlas(Assets.getText("assets/celestial-circus.atlas"), new FlixelTextureLoader("assets/celestial-circus.atlas"));
var data = SkeletonData.from(loadBinary ? Assets.getBytes("assets/celestial-circus-pro.skel") : Assets.getText("assets/celestial-circus-pro.json"), atlas, .15);
var data = SkeletonData.from(loadBinary ? Assets.getBytes("assets/celestial-circus-pro.skel") : Assets.getText("assets/celestial-circus-pro.json"),
atlas, .15);
var animationStateData = new AnimationStateData(data);
animationStateData.defaultMix = 0.25;
@ -73,32 +74,27 @@ class CelestialCircusExample extends FlxState {
var dragging:Bool = false;
var lastX:Float = 0;
var lastY:Float = 0;
override public function update(elapsed:Float):Void
{
override public function update(elapsed:Float):Void {
super.update(elapsed);
mousePosition = FlxG.mouse.getPosition();
if (FlxG.mouse.justPressed && skeletonSprite.overlapsPoint(mousePosition))
{
if (FlxG.mouse.justPressed && skeletonSprite.overlapsPoint(mousePosition)) {
dragging = true;
lastX = mousePosition.x;
lastY = mousePosition.y;
lastY = mousePosition.y;
}
if (FlxG.mouse.justReleased) dragging = false;
if (FlxG.mouse.justReleased)
dragging = false;
if (dragging)
{
if (dragging) {
skeletonSprite.x += mousePosition.x - lastX;
skeletonSprite.y += mousePosition.y - lastY;
skeletonSprite.skeleton.physicsTranslate(
mousePosition.x - lastX,
mousePosition.y - lastY,
);
skeletonSprite.skeleton.physicsTranslate(mousePosition.x - lastX, mousePosition.y - lastY,);
lastX = mousePosition.x;
lastY = mousePosition.y;
lastY = mousePosition.y;
}
}
}

View File

@ -25,11 +25,10 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package flixelExamples;
import spine.Skin;
import flixel.ui.FlxButton;
import flixel.FlxG;

View File

@ -25,11 +25,10 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package flixelExamples;
import flixel.util.FlxSave;
import flixel.math.FlxPoint;
import flixel.util.FlxColor;
@ -49,7 +48,8 @@ class ControlBonesExample extends FlxState {
var loadBinary = true;
private var controlBones = [];
private var controls:Array<FlxSprite> = [];
private var controls:Array<FlxSprite> = [];
override public function create():Void {
FlxG.cameras.bgColor = 0xffa1b2b0;
@ -92,7 +92,7 @@ class ControlBonesExample extends FlxState {
}
var point = [.0, .0];
skeletonSprite.beforeUpdateWorldTransforms = function (go) {
skeletonSprite.beforeUpdateWorldTransforms = function(go) {
for (i in 0...controls.length) {
var bone = controlBones[i];
var control = controls[i];
@ -101,7 +101,7 @@ class ControlBonesExample extends FlxState {
go.haxeWorldCoordinatesToBone(point, bone);
bone.pose.x = point[0];
bone.pose.y = point[1];
}
}
};
super.create();
@ -111,25 +111,24 @@ class ControlBonesExample extends FlxState {
var offsetX:Float = 0;
var offsetY:Float = 0;
var sprite:FlxSprite;
override public function update(elapsed:Float):Void
{
override public function update(elapsed:Float):Void {
super.update(elapsed);
mousePosition = FlxG.mouse.getPosition();
for (control in controls) {
if (FlxG.mouse.justPressed && control.overlapsPoint(mousePosition))
{
if (FlxG.mouse.justPressed && control.overlapsPoint(mousePosition)) {
sprite = control;
offsetX = mousePosition.x - sprite.x;
offsetY = mousePosition.y - sprite.y;
}
}
if (FlxG.mouse.justReleased) sprite = null;
if (FlxG.mouse.justReleased)
sprite = null;
if (sprite != null)
{
if (sprite != null) {
sprite.x = mousePosition.x - offsetX;
sprite.y = mousePosition.y - offsetY;
}

View File

@ -25,11 +25,10 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package flixelExamples;
import flixel.text.FlxText;
import flixel.ui.FlxButton;
import flixel.FlxG;
@ -76,9 +75,7 @@ class EventsExample extends FlxState {
skeletonSprite.skeleton.setupPoseBones();
add(skeletonSprite);
trackEntry.onEvent.add(
(entry, event) -> log('Custom event for ${entry.animation.name}: ${event.data.name}'));
trackEntry.onEvent.add((entry, event) -> log('Custom event for ${entry.animation.name}: ${event.data.name}'));
add(textContainer);
super.create();
@ -88,6 +85,7 @@ class EventsExample extends FlxState {
private var logs = new Array<FlxText>();
private var logsNumber = 0;
private var yOffset = 12;
private function log(text:String) {
var length = logs.length;
var newLog = new FlxText(250, 30, text);

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package flixelExamples;
@ -43,8 +43,7 @@ import flixel.FlxG;
import flixel.FlxState;
import flixel.text.FlxText;
class FlixelState extends FlxState
{
class FlixelState extends FlxState {
var spineSprite:SkeletonSprite;
var sprite:FlxSprite;
var sprite2:FlxSprite;
@ -57,8 +56,7 @@ class FlixelState extends FlxState
var scale = 4;
var speed:Float;
override public function create():Void
{
override public function create():Void {
FlxG.cameras.bgColor = 0xffa1b2b0;
// setting speed of spineboy (450 is the speed to not let him slide)
@ -76,8 +74,8 @@ class FlixelState extends FlxState
// creating the text to display overlapping state
myText = new FlxText(0, 25, 150, "", 16);
myText.alignment = CENTER;
group.add(myText);
myText.alignment = CENTER;
group.add(myText);
var button = new FlxButton(0, 0, "Next scene", () -> FlxG.switchState(() -> new BasicExample()));
button.setPosition(FlxG.width * .75, FlxG.height / 10);
@ -92,14 +90,14 @@ class FlixelState extends FlxState
// instructions
var groupInstructions = new FlxSpriteGroup();
groupInstructions.setPosition(50, 405);
groupInstructions.add(new FlxText(0, 0, 200, "Left/Right - Move", 16));
groupInstructions.add(new FlxText(0, 25, 150, "Space - Jump", 16));
groupInstructions.add(new FlxText(200, 25, 400, "Click the button for the next example", 16));
groupInstructions.add(new FlxText(0, 0, 200, "Left/Right - Move", 16));
groupInstructions.add(new FlxText(0, 25, 150, "Space - Jump", 16));
groupInstructions.add(new FlxText(200, 25, 400, "Click the button for the next example", 16));
add(groupInstructions);
// loading spineboy
var atlas = new TextureAtlas(Assets.getText("assets/spineboy.atlas"), new FlixelTextureLoader("assets/spineboy.atlas"));
var skeletondata = SkeletonData.from(Assets.getText("assets/spineboy-pro.json"), atlas, 1/scale);
var skeletondata = SkeletonData.from(Assets.getText("assets/spineboy-pro.json"), atlas, 1 / scale);
var animationStateData = new AnimationStateData(skeletondata);
spineSprite = new SkeletonSprite(skeletondata, animationStateData);
@ -166,8 +164,8 @@ class FlixelState extends FlxState
}
var justSetIdle = true;
override public function update(elapsed:Float):Void
{
override public function update(elapsed:Float):Void {
if (FlxG.overlap(spineSprite, group)) {
myText.text = "Overlapping";
} else {
@ -191,11 +189,13 @@ class FlixelState extends FlxState
var flipped = false;
var deltaX;
if (FlxG.keys.anyPressed([RIGHT])) {
if (spineSprite.flipX == true) flipped = true;
if (spineSprite.flipX == true)
flipped = true;
spineSprite.flipX = false;
}
if (FlxG.keys.anyPressed([LEFT])) {
if (spineSprite.flipX == false) flipped = true;
if (spineSprite.flipX == false)
flipped = true;
spineSprite.flipX = true;
}
@ -211,14 +211,12 @@ class FlixelState extends FlxState
spineSprite.state.setAnimationByName(0, "walk", true);
}
}
} else if (!jumping && !justSetIdle) {
justSetWalking = false;
justSetIdle = true;
spineSprite.state.setAnimationByName(0, "idle", true);
}
super.update(elapsed);
}
}

View File

@ -25,11 +25,10 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package flixelExamples;
import spine.Skin;
import flixel.ui.FlxButton;
import flixel.FlxG;
@ -43,9 +42,10 @@ import spine.atlas.TextureAtlas;
class MixAndMatchExample extends FlxState {
var loadBinary = false;
// var loadBinary = true;
// var loadBinary = true;
var skeletonSprite:SkeletonSprite;
override public function create():Void {
FlxG.cameras.bgColor = 0xffa1b2b0;
@ -54,7 +54,8 @@ class MixAndMatchExample extends FlxState {
add(button);
var atlas = new TextureAtlas(Assets.getText("assets/mix-and-match.atlas"), new FlixelTextureLoader("assets/mix-and-match.atlas"));
var data = SkeletonData.from(loadBinary ? Assets.getBytes("assets/mix-and-match-pro.skel") : Assets.getText("assets/mix-and-match-pro.json"), atlas, .5);
var data = SkeletonData.from(loadBinary ? Assets.getBytes("assets/mix-and-match-pro.skel") : Assets.getText("assets/mix-and-match-pro.json"), atlas,
.5);
var animationStateData = new AnimationStateData(data);
animationStateData.defaultMix = 0.25;
@ -80,5 +81,4 @@ class MixAndMatchExample extends FlxState {
super.create();
}
}

View File

@ -25,11 +25,10 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package flixelExamples;
import spine.Skin;
import flixel.ui.FlxButton;
import flixel.FlxG;

View File

@ -25,11 +25,10 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package flixelExamples;
import flixel.ui.FlxButton;
import flixel.FlxG;
import spine.flixel.SkeletonSprite;
@ -44,6 +43,7 @@ class SequenceExample extends FlxState {
var loadBinary = true;
var skeletonSprite:SkeletonSprite;
override public function create():Void {
FlxG.cameras.bgColor = 0xffa1b2b0;
@ -64,5 +64,4 @@ class SequenceExample extends FlxState {
add(skeletonSprite);
super.create();
}
}

View File

@ -25,11 +25,10 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package flixelExamples;
import spine.Skin;
import flixel.ui.FlxButton;
import flixel.FlxG;

View File

@ -25,11 +25,10 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package flixelExamples;
import spine.Skin;
import flixel.ui.FlxButton;
import flixel.FlxG;

View File

@ -25,11 +25,10 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package flixelExamples;
import spine.Skin;
import flixel.ui.FlxButton;
import flixel.FlxG;

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package starlingExamples;
@ -44,8 +44,9 @@ import starling.display.Quad;
class AnimationBoundExample extends Scene {
var loadBinary = false;
var skeletonSpriteClipping: SkeletonSprite;
var skeletonSpriteNoClipping: SkeletonSprite;
var skeletonSpriteClipping:SkeletonSprite;
var skeletonSpriteNoClipping:SkeletonSprite;
public function load():Void {
background.color = 0x333333;
var scale = .2;
@ -66,8 +67,8 @@ class AnimationBoundExample extends Scene {
var animationClipping = skeletonSpriteClipping.state.setAnimationByName(0, "portal", true).animation;
var animationBoundClipping = skeletonSpriteClipping.getAnimationBounds(animationClipping, true);
var quad:Quad = new Quad(animationBoundClipping.width * scale, animationBoundClipping.height * scale, 0xc70000);
quad.x = skeletonSpriteClipping.x + animationBoundClipping.x * scale;
quad.y = skeletonSpriteClipping.y + animationBoundClipping.y * scale;
quad.x = skeletonSpriteClipping.x + animationBoundClipping.x * scale;
quad.y = skeletonSpriteClipping.y + animationBoundClipping.y * scale;
var animationStateDataNoClipping = new AnimationStateData(skeletondata);
animationStateDataNoClipping.defaultMix = 0.25;
@ -80,8 +81,8 @@ class AnimationBoundExample extends Scene {
var animationNoClipping = skeletonSpriteNoClipping.state.setAnimationByName(0, "portal", true).animation;
var animationBoundNoClipping = skeletonSpriteNoClipping.getAnimationBounds(animationNoClipping, false);
var quadNoClipping:Quad = new Quad(animationBoundNoClipping.width * scale, animationBoundNoClipping.height * scale, 0xc70000);
quadNoClipping.x = skeletonSpriteNoClipping.x + animationBoundNoClipping.x * scale;
quadNoClipping.y = skeletonSpriteNoClipping.y + animationBoundNoClipping.y * scale;
quadNoClipping.x = skeletonSpriteNoClipping.x + animationBoundNoClipping.x * scale;
quadNoClipping.y = skeletonSpriteNoClipping.y + animationBoundNoClipping.y * scale;
addChild(quad);
addChild(quadNoClipping);

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package starlingExamples;

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package starlingExamples;
@ -52,7 +52,8 @@ class CelestialCircusExample extends Scene {
background.color = 0x333333;
var atlas = new TextureAtlas(Assets.getText("assets/celestial-circus.atlas"), new StarlingTextureLoader("assets/celestial-circus.atlas"));
var skeletondata = SkeletonData.from(loadBinary ? Assets.getBytes("assets/celestial-circus-pro.skel") : Assets.getText("assets/celestial-circus-pro.json"), atlas);
var skeletondata = SkeletonData.from(loadBinary ? Assets.getBytes("assets/celestial-circus-pro.skel") : Assets.getText("assets/celestial-circus-pro.json"),
atlas);
var animationStateData = new AnimationStateData(skeletondata);
animationStateData.defaultMix = 0.25;
@ -83,10 +84,7 @@ class CelestialCircusExample extends Scene {
skeletonTouch.getMovement(this, movement);
skeletonSprite.x += movement.x;
skeletonSprite.y += movement.y;
skeletonSprite.skeleton.physicsTranslate(
movement.x / skeletonSprite.scale,
movement.y / skeletonSprite.scale,
);
skeletonSprite.skeleton.physicsTranslate(movement.x / skeletonSprite.scale, movement.y / skeletonSprite.scale,);
}
} else {
var sceneTouch = e.getTouch(this);
@ -94,8 +92,5 @@ class CelestialCircusExample extends Scene {
SceneManager.getInstance().switchScene(new SnowglobeExample());
}
}
}
}

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package starlingExamples;
@ -58,7 +58,6 @@ class CloudPotExample extends Scene {
skeletonSprite.skeleton.updateWorldTransform(Physics.update);
var bounds = skeletonSprite.skeleton.getBounds();
skeletonSprite.scale = 0.2;
skeletonSprite.x = Starling.current.stage.stageWidth / 2;
skeletonSprite.y = Starling.current.stage.stageHeight / 2;

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package starlingExamples;
@ -48,11 +48,12 @@ class ControlBonesExample extends Scene {
var skeletonSprite:SkeletonSprite;
private var movement = new openfl.geom.Point();
private var controlBones = [];
private var controls = [];
private var controls = [];
public function load():Void {
var atlas = new TextureAtlas(Assets.getText("assets/stretchyman.atlas"), new StarlingTextureLoader("assets/stretchyman.atlas"));
var skeletondata = SkeletonData.from(loadBinary ? Assets.getBytes("assets/stretchyman-pro.skel") : Assets.getText("assets/stretchyman-pro.json"), atlas);
var skeletondata = SkeletonData.from(loadBinary ? Assets.getBytes("assets/stretchyman-pro.skel") : Assets.getText("assets/stretchyman-pro.json"),
atlas);
var animationStateData = new AnimationStateData(skeletondata);
animationStateData.defaultMix = 0.25;
@ -97,7 +98,7 @@ class ControlBonesExample extends Scene {
}
var point = [.0, .0];
skeletonSprite.beforeUpdateWorldTransforms = function (go) {
skeletonSprite.beforeUpdateWorldTransforms = function(go) {
for (i in 0...controls.length) {
var bone = controlBones[i];
var control = controls[i];
@ -106,7 +107,7 @@ class ControlBonesExample extends Scene {
go.haxeWorldCoordinatesToBone(point, bone);
bone.pose.x = point[0];
bone.pose.y = point[1];
}
}
};
addEventListener(TouchEvent.TOUCH, onTouch);

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package starlingExamples;
@ -66,8 +66,7 @@ class EventsExample extends Scene {
// add callback to the TrackEntry
skeletonSprite.state.setAnimationByName(0, "walk", true);
var trackEntry = skeletonSprite.state.addAnimationByName(0, "run", true, 3);
trackEntry.onEvent.add(
(entry, event) -> log('Custom event for ${entry.animation.name}: ${event.data.name}'));
trackEntry.onEvent.add((entry, event) -> log('Custom event for ${entry.animation.name}: ${event.data.name}'));
addChild(skeletonSprite);
juggler.add(skeletonSprite);
@ -83,6 +82,7 @@ class EventsExample extends Scene {
private var logs = new Array<TextField>();
private var logsNumber = 0;
private var yOffset = 12;
private function log(text:String) {
var length = logs.length;
var newLog = new TextField(250, 30, text);

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package starlingExamples;

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package starlingExamples;
@ -58,7 +58,7 @@ class SackExample extends Scene {
skeletonSprite.scale = 0.2;
skeletonSprite.x = Starling.current.stage.stageWidth / 2;
skeletonSprite.y = Starling.current.stage.stageHeight/ 2;
skeletonSprite.y = Starling.current.stage.stageHeight / 2;
skeletonSprite.state.setAnimationByName(0, "cape-follow-example", true);

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package starlingExamples;

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package starlingExamples;

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package starlingExamples;
@ -57,10 +57,9 @@ class SnowglobeExample extends Scene {
skeletonSprite.skeleton.updateWorldTransform(Physics.update);
var bounds = skeletonSprite.skeleton.getBounds();
skeletonSprite.scale = 0.15;
skeletonSprite.x = Starling.current.stage.stageWidth / 2;
skeletonSprite.y = Starling.current.stage.stageHeight/ 1.5;
skeletonSprite.y = Starling.current.stage.stageHeight / 1.5;
skeletonSprite.state.setAnimationByName(0, "shake", true);

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package starlingExamples;

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package starlingExamples;

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package starlingExamples;

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;
@ -37,7 +37,8 @@ package spine;
class Bone extends PosedActive<BoneData, BoneLocal, BonePose> {
static public var yDown:Bool = false;
static public var yDir(get, never):Int;
static private function get_yDir(): Int {
static private function get_yDir():Int {
return Bone.yDown ? -1 : 1;
}
@ -49,7 +50,7 @@ class Bone extends PosedActive<BoneData, BoneLocal, BonePose> {
public var sorted = false;
public function new (data:BoneData, parent:Bone) {
public function new(data:BoneData, parent:Bone) {
super(data, new BonePose(), new BonePose());
this.parent = parent;
applied.bone = this;

View File

@ -25,13 +25,12 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;
/** The setup pose for a bone. */
class BoneData extends PosedData<BoneLocal> {
/** The index of the bone in spine.Skeleton.getBones(). */
public final index:Int;
@ -41,6 +40,7 @@ class BoneData extends PosedData<BoneLocal> {
public var length = 0.;
// Nonessential.
/** The color of the bone as it was in Spine, or a default color if nonessential data was not exported. Bones are not usually
* rendered at runtime. */
public var color = new Color(0, 0, 0, 0);
@ -51,10 +51,12 @@ class BoneData extends PosedData<BoneLocal> {
/** False if the bone was hidden in Spine and nonessential data was exported. Does not affect runtime rendering. */
public var visible = false;
public function new (index:Int, name:String, parent:BoneData) {
public function new(index:Int, name:String, parent:BoneData) {
super(name, new BoneLocal());
if (index < 0) throw new SpineException("index must be >= 0.");
if (name == null) throw new SpineException("name cannot be null.");
if (index < 0)
throw new SpineException("index must be >= 0.");
if (name == null)
throw new SpineException("name cannot be null.");
this.index = index;
this.parent = parent;
}

View File

@ -25,13 +25,12 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;
/** Stores a bone's local pose. */
class BoneLocal implements Pose<BoneLocal> {
/** The local x translation. */
public var x:Float = 0;
@ -55,17 +54,19 @@ class BoneLocal implements Pose<BoneLocal> {
/** Determines how parent world transforms affect this bone. */
public var inherit(default, set):Inherit;
function set_inherit (value:Inherit):Inherit {
if (value == null) throw new SpineException("inherit cannot be null.");
function set_inherit(value:Inherit):Inherit {
if (value == null)
throw new SpineException("inherit cannot be null.");
inherit = value;
return value;
}
public function new () {
}
public function new() {}
public function set (pose:BoneLocal):Void {
if (pose == null) throw new SpineException("pose cannot be null.");
public function set(pose:BoneLocal):Void {
if (pose == null)
throw new SpineException("pose cannot be null.");
x = pose.x;
y = pose.y;
rotation = pose.rotation;
@ -75,5 +76,4 @@ class BoneLocal implements Pose<BoneLocal> {
shearY = pose.shearY;
inherit = pose.inherit;
}
}

View File

@ -25,14 +25,13 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;
/** The applied pose for a bone. This is the {@link Bone} pose with constraints applied and the world transform computed by
* Skeleton.updateWorldTransform(Physics). */
class BonePose extends BoneLocal implements Update {
public var bone:Bone;
/** Part of the world transform matrix for the X axis. If changed, updateAppliedTransform() should be called. */
@ -61,14 +60,15 @@ class BonePose extends BoneLocal implements Update {
// }
/** Called by Skeleton.updateCache() to compute the world transform, if needed. */
public function update (skeleton:Skeleton, physics:Physics):Void {
if (world != skeleton._update) updateWorldTransform(skeleton);
public function update(skeleton:Skeleton, physics:Physics):Void {
if (world != skeleton._update)
updateWorldTransform(skeleton);
}
/** Computes the world transform using the parent bone's applied pose and this pose. Child bones are not updated.
*
* @see https://esotericsoftware.com/spine-runtime-skeletons#World-transforms World transforms in the Spine Runtimes Guide
*/
*
* @see https://esotericsoftware.com/spine-runtime-skeletons#World-transforms World transforms in the Spine Runtimes Guide
*/
public function updateWorldTransform(skeleton:Skeleton):Void {
if (local == skeleton._update)
updateLocalTransform(skeleton);
@ -139,15 +139,19 @@ class BonePose extends BoneLocal implements Update {
c = pc * la + pd * lc;
d = pc * lb + pd * ld;
case Inherit.noScale, Inherit.noScaleOrReflection:
var r = rotation * MathUtils.degRad, cos = Math.cos(r), sin = Math.sin(r);
var r = rotation * MathUtils.degRad,
cos = Math.cos(r),
sin = Math.sin(r);
var za = (pa * cos + pb * sin) / skeleton.scaleX;
var zc = (pc * cos + pd * sin) / skeleton.scaleY;
var s = Math.sqrt(za * za + zc * zc);
if (s > 0.00001) s = 1 / s;
if (s > 0.00001)
s = 1 / s;
za *= s;
zc *= s;
s = Math.sqrt(za * za + zc * zc);
if (inherit == Inherit.noScale && ((pa * pd - pb * pc < 0) != ((skeleton.scaleX < 0) != (skeleton.scaleY < 0)))) s = -s;
if (inherit == Inherit.noScale && ((pa * pd - pb * pc < 0) != ((skeleton.scaleX < 0) != (skeleton.scaleY < 0))))
s = -s;
r = Math.PI / 2 + Math.atan2(zc, za);
var zb:Float = Math.cos(r) * s;
var zd:Float = Math.sin(r) * s;
@ -169,13 +173,13 @@ class BonePose extends BoneLocal implements Update {
}
/** Computes the applied transform values from the world transform.
*
* If the world transform is modified (by a constraint, rotateWorld(), etc) then this method should be called so
* the applied transform matches the world transform. The applied transform may be needed by other code (eg to apply another
* constraint).
*
* Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. The applied transform after
* calling this method is equivalent to the local transform used to compute the world transform, but may not be identical. */
*
* If the world transform is modified (by a constraint, rotateWorld(), etc) then this method should be called so
* the applied transform matches the world transform. The applied transform may be needed by other code (eg to apply another
* constraint).
*
* Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. The applied transform after
* calling this method is equivalent to the local transform used to compute the world transform, but may not be identical. */
public function updateLocalTransform(skeleton:Skeleton):Void {
local = 0;
world = skeleton._update;
@ -215,15 +219,19 @@ class BonePose extends BoneLocal implements Update {
ia = pd * pid;
ib = pb * pid;
case Inherit.noScale, Inherit.noScaleOrReflection:
var r = rotation * MathUtils.degRad, cos = Math.cos(rotation), sin = Math.sin(rotation);
var r = rotation * MathUtils.degRad,
cos = Math.cos(rotation),
sin = Math.sin(rotation);
pa = (pa * cos + pb * sin) / skeleton.scaleX;
pc = (pc * cos + pd * sin) / skeleton.scaleY;
var s = Math.sqrt(pa * pa + pc * pc);
if (s > 0.00001) s = 1 / s;
if (s > 0.00001)
s = 1 / s;
pa *= s;
pc *= s;
s = Math.sqrt(pa * pa + pc * pc);
if (inherit == Inherit.noScale && (pid < 0 != ((skeleton.scaleX < 0) != (skeleton.scaleY < 0)))) s = -s;
if (inherit == Inherit.noScale && (pid < 0 != ((skeleton.scaleX < 0) != (skeleton.scaleY < 0))))
s = -s;
r = MathUtils.PI / 2 + Math.atan2(pc, pa);
pb = Math.cos(r) * s;
pd = Math.sin(r) * s;
@ -255,24 +263,26 @@ class BonePose extends BoneLocal implements Update {
}
/** If the world transform has been modified and the local transform no longer matches, {@link #updateLocalTransform(Skeleton)}
* is called. */
public function validateLocalTransform (skeleton: Skeleton) {
if (local == skeleton._update) updateLocalTransform(skeleton);
* is called. */
public function validateLocalTransform(skeleton:Skeleton) {
if (local == skeleton._update)
updateLocalTransform(skeleton);
}
public function modifyLocal (skeleton: Skeleton) {
if (local == skeleton._update) updateLocalTransform(skeleton);
public function modifyLocal(skeleton:Skeleton) {
if (local == skeleton._update)
updateLocalTransform(skeleton);
world = 0;
resetWorld(skeleton._update);
}
public function modifyWorld (update:Int) {
public function modifyWorld(update:Int) {
local = update;
world = update;
resetWorld(update);
}
public function resetWorld (update:Int) {
public function resetWorld(update:Int) {
var children = bone.children;
for (i in 0...bone.children.length) {
var child = children[i].applied;
@ -331,14 +341,14 @@ class BonePose extends BoneLocal implements Update {
}
/** Transforms a point from world coordinates to the parent bone's local coordinates. */
public function worldToParent(world: Array<Float>):Array<Float> {
public function worldToParent(world:Array<Float>):Array<Float> {
if (world == null)
throw new SpineException("world cannot be null.");
return bone.parent == null ? world : bone.parent.applied.worldToLocal(world);
}
/** Transforms a point from the parent bone's coordinates to world coordinates. */
public function parentToWorld(world: Array<Float>):Array<Float> {
public function parentToWorld(world:Array<Float>):Array<Float> {
if (world == null)
throw new SpineException("world cannot be null.");
return bone.parent == null ? world : bone.parent.applied.localToWorld(world);
@ -373,7 +383,7 @@ class BonePose extends BoneLocal implements Update {
d = sin * rb + cos * d;
}
public function toString ():String {
public function toString():String {
return bone.data.name;
}
}

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;
@ -33,17 +33,16 @@ abstract class Constraint< //
T:Constraint<T, D, P>, //
D:ConstraintData<T, P>, //
P:Pose<Any>> //
extends PosedActive<D, P, P> implements Update {
public function new (data:D, pose:P, constrained:P) {
extends PosedActive<D, P, P> implements Update {
public function new(data:D, pose:P, constrained:P) {
super(data, pose, constrained);
}
public abstract function copy (skeleton:Skeleton):T;
public abstract function copy(skeleton:Skeleton):T;
public abstract function sort (skeleton:Skeleton):Void;
public abstract function sort(skeleton:Skeleton):Void;
public function isSourceActive ():Bool {
public function isSourceActive():Bool {
return true;
}
}
}

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;
@ -33,11 +33,10 @@ package spine;
abstract class ConstraintData< //
T:Constraint<Dynamic, Dynamic, Dynamic>, //
P:Pose<Any>> //
extends PosedData<P> {
extends PosedData<P> {
function new(name:String, setup:P) {
super(name, setup);
}
public abstract function create (skeleton:Skeleton):T;
public abstract function create(skeleton:Skeleton):T;
}

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;
@ -50,7 +50,8 @@ class Event {
public var balance = 0.;
public function new(time:Float, data:EventData) {
if (data == null) throw new SpineException("data cannot be null.");
if (data == null)
throw new SpineException("data cannot be null.");
this.time = time;
this.data = data;
}

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;

View File

@ -25,19 +25,23 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;
interface HasTextureRegion {
/** The name used to find the region. */
public var path:String;
/** Sets the region used to draw the attachment. After setting the region or if the region's properties are changed,
* updateRegion() must be called. */
public var region:TextureRegion;
/** The color to tint the attachment. */
public var color:Color;
public var sequence:Sequence;
/** Updates any values the attachment calculates using the region. Must be called after setting the
* region or if the region's properties are changed. */
public function updateRegion():Void;

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;
@ -34,7 +34,6 @@ package spine;
*
* @see https://esotericsoftware.com/spine-ik-constraints IK constraints in the Spine User Guide */
class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkConstraintPose> {
/** The 1 or 2 bones that will be modified by this IK constraint. */
public final bones:Array<BonePose>;
@ -43,7 +42,8 @@ class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkConstrai
public function new(data:IkConstraintData, skeleton:Skeleton) {
super(data, new IkConstraintPose(), new IkConstraintPose());
if (skeleton == null) throw new SpineException("skeleton cannot be null.");
if (skeleton == null)
throw new SpineException("skeleton cannot be null.");
bones = new Array<BonePose>();
for (boneData in data.bones)
@ -51,24 +51,27 @@ class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkConstrai
target = skeleton.bones[data.target.index];
}
public function copy (skeleton:Skeleton) {
public function copy(skeleton:Skeleton) {
var copy = new IkConstraint(data, skeleton);
copy.pose.set(pose);
return copy;
}
/** Applies the constraint to the constrained bones. */
public function update (skeleton:Skeleton, physics:Physics):Void {
public function update(skeleton:Skeleton, physics:Physics):Void {
var p = applied;
if (p.mix == 0) return;
if (p.mix == 0)
return;
var target = target.applied;
switch (bones.length) {
case 1: apply1(skeleton, bones[0], target.worldX, target.worldY, p.compress, p.stretch, data.uniform, p.mix);
case 2: apply2(skeleton, bones[0], bones[1], target.worldX, target.worldY, p.bendDirection, p.stretch, data.uniform, p.softness, p.mix);
case 1:
apply1(skeleton, bones[0], target.worldX, target.worldY, p.compress, p.stretch, data.uniform, p.mix);
case 2:
apply2(skeleton, bones[0], bones[1], target.worldX, target.worldY, p.bendDirection, p.stretch, data.uniform, p.softness, p.mix);
}
}
public function sort (skeleton:Skeleton) {
public function sort(skeleton:Skeleton) {
skeleton.sortBone(target);
var parent = bones[0].bone;
skeleton.sortBone(parent);
@ -76,24 +79,25 @@ class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkConstrai
parent.sorted = false;
skeleton.sortReset(parent.children);
skeleton.constrained(parent);
if (bones.length > 1) skeleton.constrained(bones[1].bone);
if (bones.length > 1)
skeleton.constrained(bones[1].bone);
}
override public function isSourceActive () {
override public function isSourceActive() {
return target.active;
}
public function set_target (target:Bone):Bone {
if (target == null) throw new SpineException("target cannot be null.");
public function set_target(target:Bone):Bone {
if (target == null)
throw new SpineException("target cannot be null.");
this.target = target;
return target;
}
/** Applies 1 bone IK. The target is specified in the world coordinate system. */
static public function apply1(skeleton:Skeleton, bone:BonePose, targetX:Float, targetY:Float, compress:Bool, stretch:Bool,
uniform:Bool, mix:Float) {
if (bone == null) throw new SpineException("bone cannot be null.");
static public function apply1(skeleton:Skeleton, bone:BonePose, targetX:Float, targetY:Float, compress:Bool, stretch:Bool, uniform:Bool, mix:Float) {
if (bone == null)
throw new SpineException("bone cannot be null.");
bone.modifyLocal(skeleton);
var p = bone.bone.parent.applied;
var pa = p.a, pb = p.b, pc = p.c, pd = p.d;
@ -127,7 +131,8 @@ class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkConstrai
switchDefault();
}
rotationIK += MathUtils.atan2Deg(ty, tx);
if (bone.scaleX < 0) rotationIK += 180;
if (bone.scaleX < 0)
rotationIK += 180;
if (rotationIK > 180)
rotationIK -= 360;
else if (rotationIK < -180) //
@ -139,13 +144,14 @@ class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkConstrai
tx = targetX - bone.worldX;
ty = targetY - bone.worldY;
}
var b = bone.bone.data.length * bone.scaleX;
var b = bone.bone.data.length * bone.scaleX;
if (b > 0.0001) {
var dd = tx * tx + ty * ty;
var dd = tx * tx + ty * ty;
if ((compress && dd < b * b) || (stretch && dd > b * b)) {
var s = (Math.sqrt(dd) / b - 1) * mix + 1;
bone.scaleX *= s;
if (uniform) bone.scaleY *= s;
if (uniform)
bone.scaleY *= s;
}
}
}
@ -153,12 +159,14 @@ class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkConstrai
/** Applies 2 bone IK. The target is specified in the world coordinate system.
* @param child A direct descendant of the parent bone. */
static public function apply2(skeleton:Skeleton, parent:BonePose, child:BonePose, targetX:Float, targetY:Float, bendDir:Int,
stretch:Bool, uniform:Bool, softness:Float, mix:Float):Void {
if (parent == null) throw new SpineException("parent cannot be null.");
if (child == null) throw new SpineException("child cannot be null.");
if (parent.inherit != Inherit.normal || child.inherit != Inherit.normal) return;
static public function apply2(skeleton:Skeleton, parent:BonePose, child:BonePose, targetX:Float, targetY:Float, bendDir:Int, stretch:Bool, uniform:Bool,
softness:Float, mix:Float):Void {
if (parent == null)
throw new SpineException("parent cannot be null.");
if (child == null)
throw new SpineException("child cannot be null.");
if (parent.inherit != Inherit.normal || child.inherit != Inherit.normal)
return;
parent.modifyLocal(skeleton);
child.modifyLocal(skeleton);
var px = parent.x, py = parent.y, psx = parent.scaleX, psy = parent.scaleY, csx = child.scaleX;
@ -232,8 +240,9 @@ class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkConstrai
cos = 1;
if (stretch) {
a = (Math.sqrt(dd) / (l1 + l2) - 1) * mix + 1;
parent.scaleX *= a;
if (uniform) parent.scaleY *= a;
parent.scaleX *= a;
if (uniform)
parent.scaleY *= a;
}
}
a2 = Math.acos(cos) * bendDir;

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;
@ -33,7 +33,6 @@ package spine;
*
* @see https://esotericsoftware.com/spine-ik-constraints IK constraints in the Spine User Guide */
class IkConstraintData extends ConstraintData<IkConstraint, IkConstraintPose> {
/** The bones that are constrained by this IK constraint. */
public final bones:Array<BoneData> = new Array<BoneData>();
@ -44,17 +43,17 @@ class IkConstraintData extends ConstraintData<IkConstraint, IkConstraintPose> {
* on both the X and Y axes. */
public var uniform = false;
public function new(name:String) {
super(name, new IkConstraintPose());
}
public function create (skeleton:Skeleton):IkConstraint {
public function create(skeleton:Skeleton):IkConstraint {
return new IkConstraint(this, skeleton);
}
public function set_target (target:BoneData) {
if (target == null) throw new SpineException("target cannot be null.");
public function set_target(target:BoneData) {
if (target == null)
throw new SpineException("target cannot be null.");
this.target = target;
return target;
}

View File

@ -25,13 +25,12 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;
/** Stores the current pose for an IK constraint. */
class IkConstraintPose implements Pose<IkConstraintPose> {
/** For two bone IK, controls the bend direction of the IK bones, either 1 or -1. */
public var bendDirection = 0;
@ -39,29 +38,27 @@ class IkConstraintPose implements Pose<IkConstraintPose> {
public var compress:Bool = false;
/** When true and the target is out of range, the parent bone is scaled to reach it.
*
* For two bone IK: 1) the child bone's local Y translation is set to 0, 2) stretch is not applied if softness is
* > 0, and 3) if the parent bone has local nonuniform scale, stretch is not applied. */
*
* For two bone IK: 1) the child bone's local Y translation is set to 0, 2) stretch is not applied if softness is
* > 0, and 3) if the parent bone has local nonuniform scale, stretch is not applied. */
public var stretch:Bool = false;
/** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation.
*
* For two bone IK: if the parent bone has local nonuniform scale, the child bone's local Y translation is set to 0. */
*
* For two bone IK: if the parent bone has local nonuniform scale, the child bone's local Y translation is set to 0. */
public var mix = 0.;
/** For two bone IK, the target bone's distance from the maximum reach of the bones where rotation begins to slow. The bones
* will not straighten completely until the target is this far out of range. */
* will not straighten completely until the target is this far out of range. */
public var softness = 0.;
public function new () {
}
public function new() {}
public function set (pose:IkConstraintPose) {
public function set(pose:IkConstraintPose) {
mix = pose.mix;
softness = pose.softness;
bendDirection = pose.bendDirection;
compress = pose.compress;
stretch = pose.stretch;
}
}
}

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;
@ -63,7 +63,7 @@ class MathUtils {
* @param x The x-coordinate.
* @return The arc tangent in degrees.
*/
static public function atan2Deg (y:Float, x:Float):Float {
static public function atan2Deg(y:Float, x:Float):Float {
return Math.atan2(y, x) * MathUtils.radDeg;
}

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;
@ -56,9 +56,10 @@ class PathConstraint extends Constraint<PathConstraint, PathConstraintData, Path
private final lengths = new Array<Float>();
private final segments = new Array<Float>();
public function new (data:PathConstraintData, skeleton:Skeleton) {
public function new(data:PathConstraintData, skeleton:Skeleton) {
super(data, new PathConstraintPose(), new PathConstraintPose());
if (skeleton == null) throw new SpineException("skeleton cannot be null.");
if (skeleton == null)
throw new SpineException("skeleton cannot be null.");
bones = new Array<BonePose>();
for (boneData in data.bones)
@ -76,18 +77,23 @@ class PathConstraint extends Constraint<PathConstraint, PathConstraintData, Path
/** Applies the constraint to the constrained bones. */
public function update(skeleton:Skeleton, physics:Physics):Void {
var attachment = slot.applied.attachment;
if (!Std.isOfType(attachment, PathAttachment)) return;
if (!Std.isOfType(attachment, PathAttachment))
return;
var pathAttachment = cast(attachment, PathAttachment);
var p = applied;
var mixRotate = p.mixRotate, mixX = p.mixX, mixY = p.mixY;
if (mixRotate == 0 && mixX == 0 && mixY == 0) return;
if (mixRotate == 0 && mixX == 0 && mixY == 0)
return;
var data = data;
var fTangents = data.rotateMode == RotateMode.tangent, fScale = data.rotateMode == RotateMode.chainScale;
var boneCount = bones.length, spacesCount = fTangents ? boneCount : boneCount + 1;
var fTangents = data.rotateMode == RotateMode.tangent,
fScale = data.rotateMode == RotateMode.chainScale;
var boneCount = bones.length,
spacesCount = fTangents ? boneCount : boneCount + 1;
ArrayUtils.resize(spaces, spacesCount, 0);
if (fScale) ArrayUtils.resize(lengths, boneCount, 0);
if (fScale)
ArrayUtils.resize(lengths, boneCount, 0);
var spacing = p.spacing;
var bones = bones;
@ -102,7 +108,8 @@ class PathConstraint extends Constraint<PathConstraint, PathConstraintData, Path
lengths[i] = Math.sqrt(x * x + y * y);
}
}
for (i in 1...spacesCount) spaces[i] = spacing;
for (i in 1...spacesCount)
spaces[i] = spacing;
case SpacingMode.proportional:
var sum = 0.;
var i = 0, n = spacesCount - 1;
@ -110,12 +117,14 @@ class PathConstraint extends Constraint<PathConstraint, PathConstraintData, Path
var bone = bones[i];
var setupLength:Float = bone.bone.data.length;
if (setupLength < PathConstraint.epsilon) {
if (fScale) lengths[i] = 0;
if (fScale)
lengths[i] = 0;
spaces[++i] = spacing;
} else {
var x = setupLength * bone.a, y = setupLength * bone.c;
var length = Math.sqrt(x * x + y * y);
if (fScale) lengths[i] = length;
if (fScale)
lengths[i] = length;
spaces[++i] = length;
sum += length;
}
@ -132,19 +141,23 @@ class PathConstraint extends Constraint<PathConstraint, PathConstraintData, Path
var bone = bones[i];
var setupLength = bone.bone.data.length;
if (setupLength < PathConstraint.epsilon) {
if (fScale) lengths[i] = 0;
if (fScale)
lengths[i] = 0;
spaces[++i] = spacing;
} else {
var x = setupLength * bone.a, y = setupLength * bone.c;
var length = Math.sqrt(x * x + y * y);
if (fScale) lengths[i] = length;
if (fScale)
lengths[i] = length;
spaces[++i] = (lengthSpacing ? Math.max(0, setupLength + spacing) : spacing) * length / setupLength;
}
}
}
var positions = computeWorldPositions(skeleton, pathAttachment, spacesCount, fTangents);
var boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation;
var boneX = positions[0],
boneY = positions[1],
offsetRotation = data.offsetRotation;
var tip = false;
if (offsetRotation == 0)
tip = data.rotateMode == RotateMode.chain;
@ -209,20 +222,26 @@ class PathConstraint extends Constraint<PathConstraint, PathConstraintData, Path
ArrayUtils.resize(positions, spacesCount * 3 + 2, 0);
var out:Array<Float> = positions, world = new Array<Float>();
var closed = path.closed;
var verticesLength = path.worldVerticesLength, curveCount = Std.int(verticesLength / 6), prevCurve = NONE;
var verticesLength = path.worldVerticesLength,
curveCount = Std.int(verticesLength / 6),
prevCurve = NONE;
if (!path.constantSpeed) {
var lengths = path.lengths;
curveCount -= closed ? 1 : 2;
var pathLength = lengths[curveCount];
if (data.positionMode == PositionMode.percent) position *= pathLength;
if (data.positionMode == PositionMode.percent)
position *= pathLength;
var multiplier: Float;
var multiplier:Float;
switch (data.spacingMode) {
case SpacingMode.percent: multiplier = pathLength;
case SpacingMode.proportional: multiplier = pathLength / spacesCount;
default: multiplier = 1;
case SpacingMode.percent:
multiplier = pathLength;
case SpacingMode.proportional:
multiplier = pathLength / spacesCount;
default:
multiplier = 1;
}
ArrayUtils.resize(world, 8, 0);
@ -234,7 +253,8 @@ class PathConstraint extends Constraint<PathConstraint, PathConstraintData, Path
if (closed) {
p %= pathLength;
if (p < 0) p += pathLength;
if (p < 0)
p += pathLength;
curve = 0;
} else if (p < 0) {
if (prevCurve != BEFORE) {
@ -276,8 +296,7 @@ class PathConstraint extends Constraint<PathConstraint, PathConstraintData, Path
path.computeWorldVertices(skeleton, slot, curve * 6 + 2, 8, world, 0, 2);
}
}
addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o,
tangents || (i > 0 && space == 0));
addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o, tangents || (i > 0 && space == 0));
i++;
o += 3;
}
@ -341,13 +360,17 @@ class PathConstraint extends Constraint<PathConstraint, PathConstraintData, Path
w += 6;
}
if (data.positionMode == PositionMode.percent) position *= pathLength;
if (data.positionMode == PositionMode.percent)
position *= pathLength;
var multiplier:Float;
switch (data.spacingMode) {
case SpacingMode.percent: multiplier = pathLength;
case SpacingMode.proportional: multiplier = pathLength / spacesCount;
default: multiplier = 1;
case SpacingMode.percent:
multiplier = pathLength;
case SpacingMode.proportional:
multiplier = pathLength / spacesCount;
default:
multiplier = 1;
}
var segments = segments;
@ -360,7 +383,8 @@ class PathConstraint extends Constraint<PathConstraint, PathConstraintData, Path
if (closed) {
p %= pathLength;
if (p < 0) p += pathLength;
if (p < 0)
p += pathLength;
curve = 0;
segment = 0;
} else if (p < 0) {
@ -509,10 +533,11 @@ class PathConstraint extends Constraint<PathConstraint, PathConstraintData, Path
}
}
public function sort (skeleton:Skeleton) {
public function sort(skeleton:Skeleton) {
var slotIndex = slot.data.index;
var slotBone = slot.bone;
if (skeleton.skin != null) sortPathSlot(skeleton, skeleton.skin, slotIndex, slotBone);
if (skeleton.skin != null)
sortPathSlot(skeleton, skeleton.skin, slotIndex, slotBone);
if (skeleton.data.defaultSkin != null && skeleton.data.defaultSkin != skeleton.skin)
sortPathSlot(skeleton, skeleton.data.defaultSkin, slotIndex, slotBone);
sortPath(skeleton, slot.pose.attachment, slotBone);
@ -529,15 +554,17 @@ class PathConstraint extends Constraint<PathConstraint, PathConstraintData, Path
bones[i].bone.sorted = true;
}
public function sortPathSlot (skeleton:Skeleton, skin:Skin, slotIndex:Int, slotBone:Bone) {
public function sortPathSlot(skeleton:Skeleton, skin:Skin, slotIndex:Int, slotBone:Bone) {
var entries = skin.getAttachments();
for (entry in entries) {
if (entry.slotIndex == slotIndex) sortPath(skeleton, entry.attachment, slotBone);
if (entry.slotIndex == slotIndex)
sortPath(skeleton, entry.attachment, slotBone);
}
}
private function sortPath (skeleton:Skeleton, attachment:Attachment, slotBone:Bone) {
if (!(Std.isOfType(attachment, PathAttachment))) return;
private function sortPath(skeleton:Skeleton, attachment:Attachment, slotBone:Bone) {
if (!(Std.isOfType(attachment, PathAttachment)))
return;
var pathBones = cast(attachment, PathAttachment).bones;
if (pathBones == null)
skeleton.sortBone(slotBone);
@ -553,7 +580,7 @@ class PathConstraint extends Constraint<PathConstraint, PathConstraintData, Path
}
}
override public function isSourceActive (): Bool {
override public function isSourceActive():Bool {
return slot.bone.active;
}
}

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;
@ -51,34 +51,38 @@ class PathConstraintData extends ConstraintData<PathConstraint, PathConstraintPo
/** An offset added to the constrained bone rotation. */
public var offsetRotation:Float = 0;
public function new (name:String) {
public function new(name:String) {
super(name, new PathConstraintPose());
}
public function create (skeleton:Skeleton) {
public function create(skeleton:Skeleton) {
return new PathConstraint(this, skeleton);
}
public function set_slot (slot:SlotData):SlotData {
if (slot == null) throw new SpineException("slot cannot be null.");
public function set_slot(slot:SlotData):SlotData {
if (slot == null)
throw new SpineException("slot cannot be null.");
this.slot = slot;
return slot;
}
public function set_positionMode (positionMode:PositionMode):PositionMode {
if (positionMode == null) throw new SpineException("positionMode cannot be null.");
public function set_positionMode(positionMode:PositionMode):PositionMode {
if (positionMode == null)
throw new SpineException("positionMode cannot be null.");
this.positionMode = positionMode;
return positionMode;
}
public function set_spacingMode (spacingMode:SpacingMode):SpacingMode {
if (spacingMode == null) throw new SpineException("spacingMode cannot be null.");
public function set_spacingMode(spacingMode:SpacingMode):SpacingMode {
if (spacingMode == null)
throw new SpineException("spacingMode cannot be null.");
this.spacingMode = spacingMode;
return spacingMode;
}
public function set_rotateMode (rotateMode:RotateMode):RotateMode {
if (rotateMode == null) throw new SpineException("rotateMode cannot be null.");
public function set_rotateMode(rotateMode:RotateMode):RotateMode {
if (rotateMode == null)
throw new SpineException("rotateMode cannot be null.");
this.rotateMode = rotateMode;
return rotateMode;
}

View File

@ -25,12 +25,11 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;
class PathConstraintPose implements Pose<PathConstraintPose> {
/** The position along the path. */
public var position = 0.;
@ -46,8 +45,7 @@ class PathConstraintPose implements Pose<PathConstraintPose> {
/** A percentage (0-1) that controls the mix between the constrained and unconstrained translation Y. */
public var mixY = 0.;
public function new () {
}
public function new() {}
public function set(pose:PathConstraintPose) {
position = pose.position;

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;
@ -33,10 +33,13 @@ package spine;
class Physics {
/** Physics are not updated or applied. */
public static var none(default, never):Physics = new Physics("none");
/** Physics are reset to the current pose. */
public static var reset(default, never):Physics = new Physics("reset");
/** Physics are updated and the pose from physics is applied. */
public static var update(default, never):Physics = new Physics("update");
/** Physics are not updated but the pose from physics is applied. */
public static var pose(default, never):Physics = new Physics("pose");

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;
@ -35,7 +35,6 @@ package spine;
* @see https://esotericsoftware.com/spine-physics-constraints Physics constraints in the Spine User Guide
*/
class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsConstraintData, PhysicsConstraintPose> {
/** The bone constrained by this physics constraint. */
public var bone:BonePose = null;
@ -62,9 +61,10 @@ class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsConstraintD
public var remaining = 0.;
public var lastTime = 0.;
public function new(data: PhysicsConstraintData, skeleton: Skeleton) {
public function new(data:PhysicsConstraintData, skeleton:Skeleton) {
super(data, new PhysicsConstraintPose(), new PhysicsConstraintPose());
if (skeleton == null) throw new SpineException("skeleton cannot be null.");
if (skeleton == null)
throw new SpineException("skeleton cannot be null.");
bone = skeleton.bones[data.bone.index].constrained;
}
@ -75,7 +75,7 @@ class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsConstraintD
return copy;
}
public function reset (skeleton:Skeleton) {
public function reset(skeleton:Skeleton) {
remaining = 0;
lastTime = skeleton.time;
_reset = true;
@ -95,7 +95,7 @@ class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsConstraintD
/** Translates the physics constraint so next update(Physics) forces are applied as if the bone moved an additional
* amount in world space. */
public function translate (x:Float, y:Float):Void {
public function translate(x:Float, y:Float):Void {
ux -= x;
uy -= y;
cx -= x;
@ -104,8 +104,10 @@ class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsConstraintD
/** Rotates the physics constraint so next update(Physics) forces are applied as if the bone rotated around the
* specified point in world space. */
public function rotate (x:Float, y:Float, degrees:Float):Void {
var r = degrees * MathUtils.degRad, cos = Math.cos(r), sin = Math.sin(r);
public function rotate(x:Float, y:Float, degrees:Float):Void {
var r = degrees * MathUtils.degRad,
cos = Math.cos(r),
sin = Math.sin(r);
var dx = cx - x, dy = cy - y;
translate(dx * cos - dy * sin - dx, dx * sin + dy * cos - dy);
}
@ -114,16 +116,21 @@ class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsConstraintD
public function update(skeleton:Skeleton, physics:Physics):Void {
var p = applied;
var mix = p.mix;
if (mix == 0) return;
if (mix == 0)
return;
var x = data.x > 0, y = data.y > 0, rotateOrShearX = data.rotate > 0 || data.shearX > 0, scaleX = data.scaleX > 0;
var x = data.x > 0,
y = data.y > 0,
rotateOrShearX = data.rotate > 0 || data.shearX > 0,
scaleX = data.scaleX > 0;
var l = bone.bone.data.length, t = data.step, z = 0.;
switch (physics) {
case Physics.none:
return;
case Physics.reset, Physics.update:
if (physics == Physics.reset) reset(skeleton);
if (physics == Physics.reset)
reset(skeleton);
var delta = Math.max(skeleton.time - lastTime, 0), aa = remaining;
remaining += delta;
@ -135,8 +142,8 @@ class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsConstraintD
ux = bx;
uy = by;
} else {
var a = remaining, i = p.inertia, f = skeleton.data.referenceScale, d = -1., m = 0., e = 0., ax = 0., ay = 0.,
qx = data.limit * delta, qy = qx * Math.abs(skeleton.scaleY);
var a = remaining, i = p.inertia, f = skeleton.data.referenceScale, d = -1., m = 0., e = 0., ax = 0., ay = 0., qx = data.limit * delta,
qy = qx * Math.abs(skeleton.scaleY);
qx *= Math.abs(skeleton.scaleX);
if (x || y) {
if (x) {
@ -174,8 +181,10 @@ class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsConstraintD
yLag = yOffset - ys;
}
z = Math.max(0, 1 - a / t);
if (x) bone.worldX += (xOffset - xLag * z) * mix * data.x;
if (y) bone.worldY += (yOffset - yLag * z) * mix * data.y;
if (x)
bone.worldX += (xOffset - xLag * z) * mix * data.x;
if (y)
bone.worldY += (yOffset - yLag * z) * mix * data.y;
}
if (rotateOrShearX || scaleX) {
var ca = Math.atan2(bone.c, bone.a), c = 0., s = 0., mr = 0., dx = cx - bone.worldX, dy = cy - bone.worldY;
@ -198,20 +207,23 @@ class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsConstraintD
s = Math.sin(r);
if (scaleX) {
r = l * bone.worldScaleX;
if (r > 0) scaleOffset += (dx * c + dy * s) * i / r;
if (r > 0)
scaleOffset += (dx * c + dy * s) * i / r;
}
} else {
c = Math.cos(ca);
s = Math.sin(ca);
var r = l * bone.worldScaleX - scaleLag * Math.max(0, 1 - aa / t);
if (r > 0) scaleOffset += (dx * c + dy * s) * i / r;
if (r > 0)
scaleOffset += (dx * c + dy * s) * i / r;
}
if (a >= t) {
if (d == -1) {
d = Math.pow(p.damping, 60 * t);
m = t * p.massInverse;
e = p.strength;
var w = f * p.wind, g = f * p.gravity * Bone.yDir;
var w = f * p.wind,
g = f * p.gravity * Bone.yDir;
ax = (w * skeleton.windX + g * skeleton.gravityX) * skeleton.scaleX;
ay = (w * skeleton.windY + g * skeleton.gravityY) * skeleton.scaleY;
}
@ -227,7 +239,8 @@ class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsConstraintD
rotateVelocity -= ((ax * s + ay * c) * h + rotateOffset * e) * m;
rotateOffset += rotateVelocity * t;
rotateVelocity *= d;
if (a < t) break;
if (a < t)
break;
var r:Float = rotateOffset * mr + ca;
c = Math.cos(r);
s = Math.sin(r);
@ -245,8 +258,10 @@ class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsConstraintD
cy = bone.worldY;
case Physics.pose:
z = Math.max(0, 1 - remaining / t);
if (x) bone.worldX += (xOffset - xLag * z) * mix * data.x;
if (y) bone.worldY += (yOffset - yLag * z) * mix * data.y;
if (x)
bone.worldX += (xOffset - xLag * z) * mix * data.x;
if (y)
bone.worldY += (yOffset - yLag * z) * mix * data.y;
}
if (rotateOrShearX) {
@ -291,7 +306,7 @@ class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsConstraintD
bone.modifyWorld(skeleton._update);
}
public function sort (skeleton: Skeleton) {
public function sort(skeleton:Skeleton) {
var bone = bone.bone;
skeleton.sortBone(bone);
skeleton._updateCache.push(this);
@ -299,7 +314,7 @@ class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsConstraintD
skeleton.constrained(bone);
}
override public function isSourceActive () {
override public function isSourceActive() {
return bone.bone.active;
}
}

View File

@ -25,7 +25,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;
@ -33,7 +33,6 @@ package spine;
*
* @see https://esotericsoftware.com/spine-physics-constraints Physics constraints in the Spine User Guide */
class PhysicsConstraintData extends ConstraintData<PhysicsConstraint, PhysicsConstraintPose> {
/** The bone constrained by this physics constraint. */
public var bone:BoneData;
@ -44,8 +43,10 @@ class PhysicsConstraintData extends ConstraintData<PhysicsConstraint, PhysicsCon
public var shearX = 0.;
public var limit = 0.;
public var step = 0.;
/** A percentage (0-1) that controls the mix between the constrained and unconstrained poses. */
public var mix = 0.;
public var inertiaGlobal = false;
public var strengthGlobal = false;
public var dampingGlobal = false;
@ -58,8 +59,7 @@ class PhysicsConstraintData extends ConstraintData<PhysicsConstraint, PhysicsCon
super(name, new PhysicsConstraintPose());
}
public function create (skeleton:Skeleton) {
public function create(skeleton:Skeleton) {
return new PhysicsConstraint(this, skeleton);
}
}

View File

@ -25,26 +25,25 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
*****************************************************************************/
package spine;
/** Stores a pose for a physics constraint. */
class PhysicsConstraintPose implements Pose<PhysicsConstraintPose> {
public var inertia = 0.;
public var strength = 0.;
public var damping = 0.;
public var massInverse = 0.;
public var wind = 0.;
public var gravity = 0.;
/** A percentage (0-1) that controls the mix between the constrained and unconstrained poses. */
public var mix = 0.;
public function new () {
}
public function new() {}
public function set (pose:PhysicsConstraintPose) {
public function set(pose:PhysicsConstraintPose) {
inertia = pose.inertia;
strength = pose.strength;
damping = pose.damping;

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