动画面板:新增两个动画测试按钮 + 把动画模型 API 全部落进对照表
## 功能(面板源码,重启后仍在)
- 新增 AnimationTools:
- Animate Random Props:随机骨骼/插槽/附件 → ani_api_test
旋转 0°→90°、位移 (0,0)→(120,-60)、缩放 (1,1)→(1.6,0.6)、斜切 (0,0)→(25,15)
插槽颜色 (1,1,1,1)→(1,0.2,0.2,0.25)、附件 显示→隐藏;全部只在 0f/30f
- Curve Test 6 Modes:curve_test,6 根骨骼各一条旋转轴(0°→45°,0f/30f),
分别 linear / curve 平滑(0.25,0,0.75,1) / stepped / ease-in(0.42,0,1,1) /
ease-out(0,0,0.58,1) / 对称自动(0.25,0.25,0.75,0.75)
- ObjectPanel.build 里挂载这两个按钮,复用面板自己的 wire() 点击轮询
## 对照表(tools/deobf/anchors.tsv + 混淆名对照表.md 第十一节)
- GP.I=animations、GP.D:LFT;.D:F=fps、Gl=动画(name/skeleton/timelineGroups/register)
- Gl.D/B/L/I(Gp)=旋转/位移/缩放/斜切;Gl.D/B(Gp,GU)=插槽颜色/附件;Gl.D()=绘制顺序
- FH=组;FI=时间轴(runtimeTimelineClass、keys、findKey、insertKey、invalidateRuntime)
- FN=关键帧(time、curve、c2x/c2y/c3x/c3y、setAngle/setX/setY、Color、Gn)
- FV=曲线枚举(linear/curve/stepped;'自动'=curve+控制值)
- 踩坑记录:
* 关键帧数值写在 FN.D()/B()/L() 的 float 字段,**不是** FN.D():ga(ga 是 deform 的),
否则会出现'有帧但运行时不变'
* 编辑器时间轴刻度就是**帧**,关键帧时间写 0/30,不要按 1 秒=30 帧换算
* FI.D(float,int) 建帧要求时间轴已有关键帧(先有鸡问题)
* fI.removeRange 两个 (int,int) 重载都不缩容 → 清空动画直接换新 fI
names.tsv 合并成 380 条,payload-named.zip 重产(7271 处成员引用)。
验证:全部在运行中的编辑器里逐条读回(时间轴/关键帧/曲线枚举/控制值),
重启后 pid 94663 单实例,日志确认 '[animtool] 两个动画按钮已加入'。
This commit is contained in:
1 parent
8df990a058
commit
6580611012
5 files changed
+583
No files matched your search
@@ -0,0 +1,403 @@
|
||||
package com.spineview;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 动画面板工具(正式按钮,重启后仍在):
|
||||
* - "Animate Random Props":随机骨骼/插槽/附件,把所有能 K 帧的属性做成 0f/30f 两个关键帧(值变化明显)
|
||||
* - "Curve Test 6 Modes":6 根骨骼各一条旋转轴(0°→45°,0f/30f),分别用 linear / curve 平滑 /
|
||||
* stepped / ease-in / ease-out / 对称自动 六种曲线
|
||||
*
|
||||
* 动画模型(全部来自字节码,见 混淆名对照表.md 第十一节):
|
||||
* GP.I:fI = 动画表;Gl = 动画(D:LGP 骨架、D:String 名字、D:fI 时间轴组)
|
||||
* Gl.D(Gp)=旋转、Gl.B(Gp)=位移、Gl.L(Gp)=缩放、Gl.I(Gp)=斜切
|
||||
* Gl.D(Gp,GU)=插槽颜色、Gl.B(Gp,GU)=附件切换
|
||||
* FH = 组(D:Gl、D:Gp、D:fI 时间轴);FI = 时间轴(D:Class 运行时类、D:fI 关键帧、L() 重建)
|
||||
* FN = 关键帧(D:F 时间、D:FV 曲线、B/L/I/S 贝塞尔控制值;
|
||||
* D(float)=angle、B(float)=x、L(float)=y、D:Color 颜色、D:Gn 附件)
|
||||
* FV = 曲线枚举(D=linear、B=curve、L=stepped)
|
||||
*/
|
||||
final class AnimationTools {
|
||||
private static boolean installed;
|
||||
|
||||
private AnimationTools() {
|
||||
}
|
||||
|
||||
/** 由 ObjectPanel.build 调用:加两个按钮并接上点击。 */
|
||||
static void install(final ClassLoader loader, Object skin, Object buttonsTable,
|
||||
Class<?> tableClass, final Class<?> cellClass, final Class<?> actorClass) {
|
||||
if (installed) return;
|
||||
try {
|
||||
addButton(loader, skin, buttonsTable, cellClass, actorClass, "Animate Random Props",
|
||||
new Runnable() {
|
||||
@Override public void run() {
|
||||
final ClassLoader classLoader = loader;
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override public void run() {
|
||||
try { animateRandom(classLoader); } catch (Throwable error) {
|
||||
System.err.println("[animtool] 随机动画失败: " + error);
|
||||
error.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}, "spineview-animate-random");
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
}
|
||||
});
|
||||
addButton(loader, skin, buttonsTable, cellClass, actorClass, "Curve Test 6 Modes",
|
||||
new Runnable() {
|
||||
@Override public void run() {
|
||||
final ClassLoader classLoader = loader;
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override public void run() {
|
||||
try { curveTest(classLoader); } catch (Throwable error) {
|
||||
System.err.println("[animtool] 曲线测试失败: " + error);
|
||||
error.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}, "spineview-curve-test");
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
}
|
||||
});
|
||||
installed = true;
|
||||
System.err.println("[animtool] 两个动画按钮已加入");
|
||||
} catch (Throwable error) {
|
||||
System.err.println("[animtool] 加按钮失败: " + error);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addButton(ClassLoader loader, Object skin, Object buttonsTable,
|
||||
Class<?> cellClass, Class<?> actorClass, String text, Runnable action) throws Exception {
|
||||
Class<?> textButton = Class.forName("com.badlogic.gdx.scenes.scene2d.ui.TextButton", true, loader);
|
||||
Class<?> skinClass = Class.forName("com.badlogic.gdx.scenes.scene2d.ui.Skin", true, loader);
|
||||
final Object button = textButton.getConstructor(String.class, skinClass).newInstance(text, skin);
|
||||
Object cell = find(buttonsTable.getClass(), "D", new Class<?>[] {actorClass}, cellClass)
|
||||
.invoke(buttonsTable, button);
|
||||
find(cellClass, "C", new Class<?>[0], cellClass).invoke(cell); // Cell.left()
|
||||
find(buttonsTable.getClass(), "I", new Class<?>[0], cellClass).invoke(buttonsTable);
|
||||
ObjectPanel.wire(loader, button, action);
|
||||
}
|
||||
|
||||
// ==================== 按钮 1:随机对象的所有可 K 属性 ====================
|
||||
|
||||
static void animateRandom(ClassLoader loader) throws Exception {
|
||||
final ClassLoader L = loader;
|
||||
final Class<?> gp = Class.forName("GP", true, L);
|
||||
final Class<?> boneC = Class.forName("Gp", true, L);
|
||||
final Class<?> slotC = Class.forName("GU", true, L);
|
||||
final Class<?> attC = Class.forName("Gn", true, L);
|
||||
final Class<?> glC = Class.forName("Gl", true, L);
|
||||
final Class<?> fiC = Class.forName("FI", true, L);
|
||||
final Class<?> fnC = Class.forName("FN", true, L);
|
||||
final Class<?> colorC = Class.forName("com.badlogic.gdx.graphics.Color", true, L);
|
||||
SceneActions.onEditorThread(L, new Runnable() {
|
||||
@Override public void run() {
|
||||
try {
|
||||
Object skeleton = null;
|
||||
List<Object> bones = new ArrayList<Object>(), slots = new ArrayList<Object>(),
|
||||
atts = new ArrayList<Object>();
|
||||
for (Object o : SceneObjects.listAllNodes(L)) {
|
||||
if (o == null) continue;
|
||||
if (skeleton == null && gp.isInstance(o)) skeleton = o;
|
||||
if (boneC.isInstance(o)) bones.add(o);
|
||||
if (slotC.isInstance(o)) slots.add(o);
|
||||
if (attC.isInstance(o)) atts.add(o);
|
||||
}
|
||||
if (skeleton == null || bones.isEmpty() || slots.isEmpty() || atts.isEmpty()) {
|
||||
System.err.println("[animtool] 场景对象不够");
|
||||
return;
|
||||
}
|
||||
Random random = new Random();
|
||||
Object bone = bones.get(random.nextInt(bones.size()));
|
||||
Object slot = slots.get(random.nextInt(slots.size()));
|
||||
Object slotBone = fieldOfType(slotC, slot, boneC);
|
||||
List<Object> own = new ArrayList<Object>();
|
||||
for (Object a : atts) if (fieldOfType(attC, a, slotC) == slot) own.add(a);
|
||||
Object attachment = own.isEmpty() ? atts.get(random.nextInt(atts.size()))
|
||||
: own.get(random.nextInt(own.size()));
|
||||
System.err.println("[animtool] 随机选中:骨骼=" + SceneObjects.nameOf(bone)
|
||||
+ " 插槽=" + SceneObjects.nameOf(slot) + " 附件=" + SceneObjects.nameOf(attachment));
|
||||
|
||||
Object anim = animation(L, skeleton, "ani_api_test");
|
||||
set(anim, "D", Class.forName("fI", true, L),
|
||||
Class.forName("fI", true, L).getConstructor().newInstance());
|
||||
Object rotate = find(glC, "D", new Class<?>[] {boneC}, fiC).invoke(anim, bone);
|
||||
scalarPair(L, rotate, fiC, fnC, 0f, "D", 0f);
|
||||
scalarPair(L, rotate, fiC, fnC, 30f, "D", 90f);
|
||||
Object translate = find(glC, "B", new Class<?>[] {boneC}, fiC).invoke(anim, bone);
|
||||
xy(L, translate, fiC, fnC, 0f, 0f, 0f);
|
||||
xy(L, translate, fiC, fnC, 30f, 120f, -60f);
|
||||
Object scale = find(glC, "L", new Class<?>[] {boneC}, fiC).invoke(anim, bone);
|
||||
xy(L, scale, fiC, fnC, 0f, 1f, 1f);
|
||||
xy(L, scale, fiC, fnC, 30f, 1.6f, 0.6f);
|
||||
Object shear = find(glC, "I", new Class<?>[] {boneC}, fiC).invoke(anim, bone);
|
||||
xy(L, shear, fiC, fnC, 0f, 0f, 0f);
|
||||
xy(L, shear, fiC, fnC, 30f, 25f, 15f);
|
||||
Object color = find(glC, "D", new Class<?>[] {boneC, slotC}, fiC).invoke(anim, slotBone, slot);
|
||||
color(L, color, fiC, fnC, colorC, 0f, 1f, 1f, 1f, 1f);
|
||||
color(L, color, fiC, fnC, colorC, 30f, 1f, 0.2f, 0.2f, 0.25f);
|
||||
Object att = find(glC, "B", new Class<?>[] {boneC, slotC}, fiC).invoke(anim, slotBone, slot);
|
||||
attachment(L, att, fiC, fnC, attC, 0f, attachment);
|
||||
attachment(L, att, fiC, fnC, attC, 30f, null);
|
||||
refresh(L, skeleton);
|
||||
System.err.println("[animtool] ani_api_test 已重建:6 条时间轴(0f/30f)");
|
||||
} catch (Throwable error) {
|
||||
System.err.println("[animtool] 随机动画失败: " + error);
|
||||
error.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== 按钮 2:六种曲线 ====================
|
||||
|
||||
static void curveTest(ClassLoader loader) throws Exception {
|
||||
final ClassLoader L = loader;
|
||||
final Class<?> gp = Class.forName("GP", true, L);
|
||||
final Class<?> boneC = Class.forName("Gp", true, L);
|
||||
final Class<?> glC = Class.forName("Gl", true, L);
|
||||
final Class<?> fiC = Class.forName("FI", true, L);
|
||||
final Class<?> fnC = Class.forName("FN", true, L);
|
||||
final Class<?> fvC = Class.forName("FV", true, L);
|
||||
SceneActions.onEditorThread(L, new Runnable() {
|
||||
@Override public void run() {
|
||||
try {
|
||||
Object skeleton = null;
|
||||
Map<String, Object> bones = new LinkedHashMap<String, Object>();
|
||||
for (Object o : SceneObjects.listAllNodes(L)) {
|
||||
if (o == null) continue;
|
||||
if (skeleton == null && gp.isInstance(o)) skeleton = o;
|
||||
if (boneC.isInstance(o)) bones.put(SceneObjects.nameOf(o), o);
|
||||
}
|
||||
if (skeleton == null) return;
|
||||
String[] order = {"root", "bone", "bone2", "bone3", "bone5", "lizi"};
|
||||
String[] labels = {"linear", "curve smooth", "stepped", "ease-in", "ease-out", "symmetric auto"};
|
||||
Object linear = fvC.getField("D").get(null);
|
||||
Object curve = fvC.getField("B").get(null);
|
||||
Object stepped = fvC.getField("L").get(null);
|
||||
Object[] modes = {linear, curve, stepped, curve, curve, curve};
|
||||
float[][] controls = {{0, 0, 0, 0}, {0.25f, 0f, 0.75f, 1f}, {0, 0, 0, 0},
|
||||
{0.42f, 0f, 1f, 1f}, {0f, 0f, 0.58f, 1f}, {0.25f, 0.25f, 0.75f, 0.75f}};
|
||||
Object anim = animation(L, skeleton, "curve_test");
|
||||
set(anim, "D", Class.forName("fI", true, L),
|
||||
Class.forName("fI", true, L).getConstructor().newInstance());
|
||||
int made = 0;
|
||||
for (int i = 0; i < order.length; i++) {
|
||||
Object bone = bones.get(order[i]);
|
||||
if (bone == null) continue;
|
||||
Object tl = find(glC, "D", new Class<?>[] {boneC}, fiC).invoke(anim, bone);
|
||||
Object k0 = key(L, tl, fiC, fnC, 0f);
|
||||
Object k1 = key(L, tl, fiC, fnC, 30f);
|
||||
setFloatMethod(fnC, k0, "D", 0f); // angle 0
|
||||
setFloatMethod(fnC, k1, "D", 45f); // angle 45
|
||||
set(k0, "D", fvC, modes[i]);
|
||||
set(k0, "B", float.class, Float.valueOf(controls[i][0]));
|
||||
set(k0, "L", float.class, Float.valueOf(controls[i][1]));
|
||||
set(k0, "I", float.class, Float.valueOf(controls[i][2]));
|
||||
set(k0, "S", float.class, Float.valueOf(controls[i][3]));
|
||||
rebuild(fiC, tl);
|
||||
System.err.println("[animtool] curve_test " + order[i] + " = " + labels[i]);
|
||||
made++;
|
||||
}
|
||||
refresh(L, skeleton);
|
||||
System.err.println("[animtool] curve_test 已重建:共 " + made + " 条旋转轴(0f/30f,0°→45°)");
|
||||
} catch (Throwable error) {
|
||||
System.err.println("[animtool] 曲线测试失败: " + error);
|
||||
error.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== 时间轴/关键帧 ====================
|
||||
|
||||
/** 取或建动画(并按编辑器自己的 Gl.D() 注册)。 */
|
||||
private static Object animation(ClassLoader L, Object skeleton, String name) throws Exception {
|
||||
Class<?> gp = Class.forName("GP", true, L);
|
||||
Class<?> glC = Class.forName("Gl", true, L);
|
||||
Class<?> fI = Class.forName("fI", true, L);
|
||||
Field animsF = gp.getDeclaredField("I");
|
||||
animsF.setAccessible(true);
|
||||
Object anims = animsF.get(skeleton);
|
||||
for (int i = 0; i < size(fI, anims); i++) {
|
||||
Object a = get(fI, anims, i);
|
||||
if (a != null && name.equals(fieldOfType(glC, a, String.class))) return a;
|
||||
}
|
||||
Object anim = glC.getConstructor().newInstance();
|
||||
set(anim, "D", gp, skeleton);
|
||||
set(anim, "D", String.class, name);
|
||||
for (Method m : glC.getDeclaredMethods()) {
|
||||
if (m.getName().equals("D") && m.getParameterTypes().length == 0
|
||||
&& m.getReturnType() == void.class) { m.setAccessible(true); m.invoke(anim); }
|
||||
}
|
||||
add(fI, anims, anim);
|
||||
System.err.println("[animtool] 新建动画 " + name);
|
||||
return anim;
|
||||
}
|
||||
|
||||
private static Object key(ClassLoader L, Object tl, Class<?> fiC, Class<?> fnC, float time)
|
||||
throws Exception {
|
||||
Method get = find(fiC, "D", new Class<?>[] {float.class}, fnC);
|
||||
Object k = get.invoke(tl, Float.valueOf(time));
|
||||
if (k != null) return k;
|
||||
k = fnC.getConstructor(fiC).newInstance(tl);
|
||||
set(k, "D", float.class, Float.valueOf(time));
|
||||
find(fiC, "D", new Class<?>[] {fnC, boolean.class}, void.class)
|
||||
.invoke(tl, k, Boolean.FALSE);
|
||||
return k;
|
||||
}
|
||||
|
||||
private static void scalarPair(ClassLoader L, Object tl, Class<?> fiC, Class<?> fnC,
|
||||
float time, String setter, float value) throws Exception {
|
||||
Object k = key(L, tl, fiC, fnC, time);
|
||||
setFloatMethod(fnC, k, setter, value);
|
||||
rebuild(fiC, tl);
|
||||
}
|
||||
|
||||
private static void xy(ClassLoader L, Object tl, Class<?> fiC, Class<?> fnC,
|
||||
float time, float x, float y) throws Exception {
|
||||
Object k = key(L, tl, fiC, fnC, time);
|
||||
setFloatMethod(fnC, k, "B", x);
|
||||
setFloatMethod(fnC, k, "L", y);
|
||||
rebuild(fiC, tl);
|
||||
}
|
||||
|
||||
private static void color(ClassLoader L, Object tl, Class<?> fiC, Class<?> fnC, Class<?> colorC,
|
||||
float time, float r, float g, float b, float a) throws Exception {
|
||||
Object k = key(L, tl, fiC, fnC, time);
|
||||
Object color = fieldOfType(fnC, k, colorC);
|
||||
if (color == null) color = colorC.getConstructor().newInstance();
|
||||
colorC.getField("r").setFloat(color, r);
|
||||
colorC.getField("g").setFloat(color, g);
|
||||
colorC.getField("b").setFloat(color, b);
|
||||
colorC.getField("a").setFloat(color, a);
|
||||
set(k, "D", colorC, color);
|
||||
rebuild(fiC, tl);
|
||||
}
|
||||
|
||||
private static void attachment(ClassLoader L, Object tl, Class<?> fiC, Class<?> fnC,
|
||||
Class<?> attC, float time, Object attachment) throws Exception {
|
||||
Object k = key(L, tl, fiC, fnC, time);
|
||||
set(k, "D", attC, attachment);
|
||||
rebuild(fiC, tl);
|
||||
}
|
||||
|
||||
private static void rebuild(Class<?> fiC, Object tl) throws Exception {
|
||||
find(fiC, "L", new Class<?>[0], void.class).invoke(tl);
|
||||
}
|
||||
|
||||
private static void refresh(ClassLoader L, Object skeleton) throws Exception {
|
||||
Class<?> gp = Class.forName("GP", true, L);
|
||||
Class<?> vz = Class.forName("VZ", true, L);
|
||||
Class<?> og = Class.forName("OG", true, L);
|
||||
Object tree = staticField(og, vz);
|
||||
Method refresh = find(vz, "D", new Class<?>[] {gp, boolean.class, boolean.class}, void.class);
|
||||
if (refresh != null) refresh.invoke(tree, skeleton, Boolean.TRUE, Boolean.TRUE);
|
||||
}
|
||||
|
||||
// ==================== 反射工具 ====================
|
||||
|
||||
private static void setFloatMethod(Class<?> owner, Object target, String name, float value)
|
||||
throws Exception {
|
||||
for (Class<?> c = owner; c != null && c != Object.class; c = c.getSuperclass()) {
|
||||
for (Method m : c.getDeclaredMethods()) {
|
||||
if (!m.getName().equals(name)) continue;
|
||||
Class<?>[] types = m.getParameterTypes();
|
||||
if (types.length != 1 || types[0] != float.class) continue;
|
||||
m.setAccessible(true);
|
||||
m.invoke(target, Float.valueOf(value));
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new NoSuchMethodException("float " + name);
|
||||
}
|
||||
|
||||
private static Object fieldOfType(Class<?> owner, Object target, Class<?> type) {
|
||||
for (Class<?> c = owner; c != null && c != Object.class; c = c.getSuperclass()) {
|
||||
for (Field f : c.getDeclaredFields()) {
|
||||
if (Modifier.isStatic(f.getModifiers()) || f.getType() != type) continue;
|
||||
f.setAccessible(true);
|
||||
try {
|
||||
Object v = f.get(target);
|
||||
if (v != null) return v;
|
||||
} catch (Throwable ignored) {
|
||||
// 下一个
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Object staticField(Class<?> owner, Class<?> type) {
|
||||
for (Field f : owner.getDeclaredFields()) {
|
||||
if (!Modifier.isStatic(f.getModifiers()) || f.getType() != type) continue;
|
||||
f.setAccessible(true);
|
||||
try {
|
||||
Object v = f.get(null);
|
||||
if (v != null) return v;
|
||||
} catch (Throwable ignored) {
|
||||
// 下一个
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void set(Object target, String name, Class<?> type, Object value) throws Exception {
|
||||
for (Class<?> c = target.getClass(); c != null && c != Object.class; c = c.getSuperclass()) {
|
||||
for (Field f : c.getDeclaredFields()) {
|
||||
if (!f.getName().equals(name) || f.getType() != type) continue;
|
||||
f.setAccessible(true);
|
||||
f.set(target, value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(name);
|
||||
}
|
||||
|
||||
private static Method find(Class<?> owner, String name, Class<?>[] params, Class<?> ret) {
|
||||
for (Class<?> c = owner; c != null && c != Object.class; c = c.getSuperclass()) {
|
||||
for (Method m : c.getDeclaredMethods()) {
|
||||
if (!m.getName().equals(name)) continue;
|
||||
if (!Arrays.equals(m.getParameterTypes(), params)) continue;
|
||||
if (ret != null && m.getReturnType() != ret) continue;
|
||||
m.setAccessible(true);
|
||||
return m;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException(owner.getName() + "." + name);
|
||||
}
|
||||
|
||||
private static void add(Class<?> arrayClass, Object array, Object item) throws Exception {
|
||||
find(arrayClass, "D", new Class<?>[] {Object.class}, void.class).invoke(array, item);
|
||||
}
|
||||
|
||||
private static int size(Class<?> arrayClass, Object array) {
|
||||
if (array == null) return 0;
|
||||
for (Class<?> c = arrayClass; c != null && c != Object.class; c = c.getSuperclass()) {
|
||||
for (Field f : c.getDeclaredFields()) {
|
||||
if (f.getType() != int.class) continue;
|
||||
f.setAccessible(true);
|
||||
try { return f.getInt(array); } catch (Throwable ignored) { }
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static Object get(Class<?> arrayClass, Object array, int index) {
|
||||
try {
|
||||
return find(arrayClass, "D", new Class<?>[] {int.class}, Object.class)
|
||||
.invoke(array, Integer.valueOf(index));
|
||||
} catch (Throwable ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,6 +138,8 @@ public final class ObjectPanel {
|
||||
}
|
||||
// 新按钮:点一下弹名称面板 → 按名字匹配附件 → 在当前选中骨骼下建 15 根骨骼(每根 1 插槽 1 附件)
|
||||
NewFifteen.install(loader, skin, buttons, tableClass, cellClass, actorClass);
|
||||
// 动画工具按钮:随机对象 6 属性动画 / 六种曲线测试
|
||||
AnimationTools.install(loader, skin, buttons, tableClass, cellClass, actorClass);
|
||||
call(buttons, Obf.TABLE_ROW, new Class<?>[0], cellClass);
|
||||
Object nameCell = call(buttons, Obf.TABLE_ADD, new Class<?>[] {actorClass}, cellClass, state.nameField);
|
||||
layoutCell(nameCell, Obf.CELL_LEFT);
|
||||
|
||||
Reference in new issue
Block a user