Files
goon-game/app/src/main/java/com/projectsacrifice/game/VolunteerCanvasRenderer.java
T

200 lines
10 KiB
Java

package com.projectsacrifice.game;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
/** Canvas-only adapter for the normalized volunteer skeleton. */
final class VolunteerCanvasRenderer {
private static final int FRAME_COUNT = 16;
private static final VolunteerRig.Pose[][][][] POSES = buildPoseCache();
void draw(Canvas canvas, Paint fill, Paint outline, Path path,
float x, float y, float width, float height,
VolunteerRig.Skin skin, VolunteerRig.Motion motion,
float phase, int facing, float alpha, boolean highContrast) {
VolunteerRig.Pose pose = cachedPose(skin, motion, phase, facing);
int frameColor = highContrast ? Color.WHITE : pose.palette().frame();
int shellColor = highContrast ? Color.rgb(255, 214, 77) : pose.palette().shell();
int clothColor = highContrast ? Color.WHITE : pose.palette().cloth();
int glowColor = highContrast ? Color.BLACK : pose.palette().glow();
outline.setStyle(Paint.Style.STROKE);
outline.setStrokeCap(Paint.Cap.ROUND);
outline.setStrokeWidth(Math.max(3f, width * 0.075f));
outline.setColor(withAlpha(frameColor, alpha));
line(canvas, outline, pose, VolunteerRig.Joint.HIPS, VolunteerRig.Joint.CHEST,
x, y, width, height);
line(canvas, outline, pose, VolunteerRig.Joint.LEFT_SHOULDER,
VolunteerRig.Joint.LEFT_HAND, x, y, width, height);
line(canvas, outline, pose, VolunteerRig.Joint.RIGHT_SHOULDER,
VolunteerRig.Joint.RIGHT_HAND, x, y, width, height);
line(canvas, outline, pose, VolunteerRig.Joint.HIPS,
VolunteerRig.Joint.LEFT_KNEE, x, y, width, height);
line(canvas, outline, pose, VolunteerRig.Joint.LEFT_KNEE,
VolunteerRig.Joint.LEFT_FOOT, x, y, width, height);
line(canvas, outline, pose, VolunteerRig.Joint.HIPS,
VolunteerRig.Joint.RIGHT_KNEE, x, y, width, height);
line(canvas, outline, pose, VolunteerRig.Joint.RIGHT_KNEE,
VolunteerRig.Joint.RIGHT_FOOT, x, y, width, height);
float chestX = px(pose, VolunteerRig.Joint.CHEST, x, width);
float chestY = py(pose, VolunteerRig.Joint.CHEST, y, height);
float hipsX = px(pose, VolunteerRig.Joint.HIPS, x, width);
float hipsY = py(pose, VolunteerRig.Joint.HIPS, y, height);
float headX = px(pose, VolunteerRig.Joint.HEAD, x, width);
float headY = py(pose, VolunteerRig.Joint.HEAD, y, height);
fill.setColor(withAlpha(clothColor, alpha * 0.86f));
path.reset();
path.moveTo(chestX - width * 0.22f * facing, chestY + height * 0.04f);
path.lineTo(hipsX - width * 0.36f * facing, hipsY + height * 0.17f);
path.lineTo(hipsX, hipsY + height * 0.06f);
path.close();
canvas.drawPath(path, fill);
fill.setColor(withAlpha(shellColor, alpha));
canvas.drawRoundRect(chestX - width * 0.22f, chestY - height * 0.08f,
chestX + width * 0.22f, hipsY + height * 0.04f,
width * 0.13f, width * 0.13f, fill);
canvas.drawCircle(headX, headY, width * 0.22f, fill);
outline.setStrokeWidth(Math.max(2f, width * 0.045f));
canvas.drawCircle(headX, headY, width * 0.22f, outline);
canvas.drawRoundRect(chestX - width * 0.22f, chestY - height * 0.08f,
chestX + width * 0.22f, hipsY + height * 0.04f,
width * 0.13f, width * 0.13f, outline);
fill.setColor(withAlpha(pose.visorLit() ? glowColor : frameColor,
pose.visorLit() ? alpha : alpha * 0.72f));
canvas.drawRoundRect(headX - width * 0.14f, headY - height * 0.025f,
headX + width * 0.14f, headY + height * 0.025f,
width * 0.025f, width * 0.025f, fill);
drawJoint(canvas, fill, pose, VolunteerRig.Joint.LEFT_SHOULDER,
x, y, width, height, frameColor, alpha);
drawJoint(canvas, fill, pose, VolunteerRig.Joint.RIGHT_SHOULDER,
x, y, width, height, frameColor, alpha);
drawJoint(canvas, fill, pose, VolunteerRig.Joint.LEFT_KNEE,
x, y, width, height, frameColor, alpha);
drawJoint(canvas, fill, pose, VolunteerRig.Joint.RIGHT_KNEE,
x, y, width, height, frameColor, alpha);
if (pose.anchorBolt()) {
fill.setColor(withAlpha(glowColor, alpha));
canvas.drawCircle(hipsX, hipsY, width * 0.14f, fill);
outline.setColor(withAlpha(frameColor, alpha));
canvas.drawLine(hipsX - width * 0.1f, hipsY, hipsX + width * 0.1f, hipsY, outline);
canvas.drawLine(hipsX, hipsY - width * 0.1f, hipsX, hipsY + width * 0.1f, outline);
}
if (pose.frozenShell()) drawFrozenShell(canvas, fill, outline, x, y, width, height, alpha);
if (pose.conductiveArcs()) drawConductiveArcs(canvas, outline, path,
x, y, width, height, phase, alpha);
if (motion == VolunteerRig.Motion.RESPAWN) drawAssemblyRings(canvas, outline,
x, y, width, height, pose.assemblyProgress(), alpha);
}
private static void drawFrozenShell(Canvas canvas, Paint fill, Paint outline,
float x, float y, float width, float height, float alpha) {
fill.setColor(withAlpha(Color.rgb(85, 205, 236), alpha * 0.34f));
canvas.drawRoundRect(x - width * 0.12f, y + height * 0.02f,
x + width * 1.12f, y + height,
width * 0.12f, width * 0.12f, fill);
outline.setColor(withAlpha(Color.rgb(218, 252, 255), alpha));
outline.setStrokeWidth(Math.max(2f, width * 0.045f));
canvas.drawLine(x, y + height * 0.18f, x + width * 0.28f, y + height * 0.45f, outline);
canvas.drawLine(x + width, y + height * 0.16f,
x + width * 0.72f, y + height * 0.52f, outline);
}
private static void drawConductiveArcs(Canvas canvas, Paint outline, Path path,
float x, float y, float width, float height,
float phase, float alpha) {
outline.setColor(withAlpha(Color.rgb(255, 239, 74), alpha));
outline.setStrokeWidth(Math.max(3f, width * 0.055f));
float pulse = ((int) (phase * 12f) & 1) == 0 ? 0f : width * 0.06f;
path.reset();
path.moveTo(x - width * 0.15f, y + height * 0.3f);
path.lineTo(x + width * 0.08f, y + height * 0.22f + pulse);
path.lineTo(x - width * 0.02f, y + height * 0.52f);
path.lineTo(x + width * 0.18f, y + height * 0.68f - pulse);
canvas.drawPath(path, outline);
path.reset();
path.moveTo(x + width * 1.15f, y + height * 0.28f);
path.lineTo(x + width * 0.91f, y + height * 0.43f - pulse);
path.lineTo(x + width * 1.03f, y + height * 0.66f);
path.lineTo(x + width * 0.82f, y + height * 0.77f + pulse);
canvas.drawPath(path, outline);
}
private static void drawAssemblyRings(Canvas canvas, Paint outline,
float x, float y, float width, float height,
float progress, float alpha) {
outline.setColor(withAlpha(Color.rgb(116, 232, 213), alpha * (1f - progress * 0.3f)));
outline.setStrokeWidth(Math.max(2f, width * 0.045f));
float cx = x + width * 0.5f;
float cy = y + height * 0.55f;
canvas.drawCircle(cx, cy, width * (0.25f + progress * 0.45f), outline);
canvas.drawCircle(cx, cy, width * (0.48f + progress * 0.35f), outline);
}
private static void drawJoint(Canvas canvas, Paint fill, VolunteerRig.Pose pose,
VolunteerRig.Joint joint, float x, float y,
float width, float height, int color, float alpha) {
fill.setColor(withAlpha(color, alpha));
canvas.drawCircle(px(pose, joint, x, width), py(pose, joint, y, height),
Math.max(2.5f, width * 0.065f), fill);
}
private static void line(Canvas canvas, Paint outline, VolunteerRig.Pose pose,
VolunteerRig.Joint from, VolunteerRig.Joint to,
float x, float y, float width, float height) {
canvas.drawLine(px(pose, from, x, width), py(pose, from, y, height),
px(pose, to, x, width), py(pose, to, y, height), outline);
}
private static float px(VolunteerRig.Pose pose, VolunteerRig.Joint joint,
float x, float width) {
return x + pose.x(joint) * width;
}
private static float py(VolunteerRig.Pose pose, VolunteerRig.Joint joint,
float y, float height) {
return y + pose.y(joint) * height;
}
private static VolunteerRig.Pose cachedPose(VolunteerRig.Skin skin,
VolunteerRig.Motion motion,
float phase, int facing) {
int frame = Math.min(FRAME_COUNT - 1,
Math.max(0, (int) ((phase - Math.floor(phase)) * FRAME_COUNT)));
return POSES[skin.ordinal()][motion.ordinal()][facing < 0 ? 0 : 1][frame];
}
private static VolunteerRig.Pose[][][][] buildPoseCache() {
VolunteerRig.Skin[] skins = VolunteerRig.Skin.values();
VolunteerRig.Motion[] motions = VolunteerRig.Motion.values();
VolunteerRig.Pose[][][][] result =
new VolunteerRig.Pose[skins.length][motions.length][2][FRAME_COUNT];
for (VolunteerRig.Skin skin : skins) {
for (VolunteerRig.Motion motion : motions) {
for (int facingIndex = 0; facingIndex < 2; facingIndex++) {
for (int frame = 0; frame < FRAME_COUNT; frame++) {
result[skin.ordinal()][motion.ordinal()][facingIndex][frame] =
VolunteerRig.pose(skin, motion, frame / (float) (FRAME_COUNT - 1),
facingIndex == 0 ? -1 : 1);
}
}
}
}
return result;
}
private static int withAlpha(int color, float alpha) {
int clamped = Math.max(0, Math.min(255, Math.round(Color.alpha(color) * alpha)));
return Color.argb(clamped, Color.red(color), Color.green(color), Color.blue(color));
}
}