Compare commits

...

30 commits

Author SHA1 Message Date
6d495d8307 input hand troubleshooting 2024-11-11 00:15:43 -05:00
56829852dc don't show hangin on float 2024-11-11 00:00:14 -05:00
5f49d94910 hole flip trick, with box mat chain 2024-11-10 23:49:00 -05:00
470fa38107 holes backface when box face does 2024-11-10 15:38:40 -05:00
2eb15dd992 simulator orbital view 2024-11-10 07:06:15 -05:00
a906d2bfdc drop fresnel intensity on box 2024-11-10 06:36:46 -05:00
cfbd489c22 fresnel effect 2024-11-10 06:34:40 -05:00
17f38b83bd polished hole 2024-11-10 06:34:31 -05:00
9aeafa7ba0 flip input normals to treat backfaces like normal 2024-11-10 05:31:31 -05:00
ffc9e3fecb proper settings for additive style mat 2024-11-10 05:24:37 -05:00
4a7f536999 draw hole backfaces 2024-11-10 05:24:13 -05:00
5b96fa3df3 version bump for refactor 2024-11-10 05:10:12 -05:00
a81cc0073a stash inset cell function away 2024-11-10 05:08:57 -05:00
5b0a096502 don't move if next pos is not in or around box 2024-11-10 05:08:05 -05:00
c1f0bcc956 refactor for around box validity 2024-11-10 05:07:28 -05:00
3fdea357fc my tempo idea 2024-11-10 04:48:52 -05:00
6121d333cf subtarget 2024-11-10 04:48:36 -05:00
11ac4207e9 step if tracked 2024-11-10 04:48:05 -05:00
196ba2955a bug killed todo done 2024-11-10 04:23:22 -05:00
7dc2cfe6a4 version increment 2024-11-10 04:21:58 -05:00
b6e20580e6 lerp food in 2024-11-10 04:21:17 -05:00
2900070ea9 eaten latch delta bool 2024-11-10 04:20:58 -05:00
9b5a17cb0c undocument bug 2024-11-10 04:03:23 -05:00
c87d56ef43 unstretch nose 2024-11-10 04:02:32 -05:00
350e4f734b hide food on eaten latch 2024-11-10 03:58:10 -05:00
40d3780319 eaten latch 2024-11-10 03:54:50 -05:00
f696952488 feed cell viability tuple 2024-11-10 03:54:34 -05:00
bddb770ef6 eat tail 2024-11-10 03:37:53 -05:00
f6e3792cb1 step from head to tail_fill zero *food path finding 2024-11-10 03:37:17 -05:00
857e32e5fe GetShuffledIndices function, to remove direction search bias 2024-11-10 03:35:36 -05:00
7 changed files with 181 additions and 89 deletions

View file

@ -8,11 +8,15 @@ float4 color;
struct vsIn {
float4 pos : SV_Position;
float3 norm : NORMAL0;
float4 col : COLOR0;
// float4 col : COLOR0;
};
struct psIn {
float4 pos : SV_POSITION;
float4 color : COLOR0;
float3 normal : NORMAL0;
float3 world_pos : WORLD;
float3 cam_pos : TEXCOORD1;
float3 cam_dir : TEXCOORD2;
uint view_id : SV_RenderTargetArrayIndex;
};
@ -21,23 +25,26 @@ psIn vs(vsIn input, uint id : SV_InstanceID) {
o.view_id = id % sk_view_count;
id = id / sk_view_count;
o.cam_pos = sk_camera_pos[o.view_id].xyz;
o.cam_dir = sk_camera_dir[o.view_id].xyz;
float3 world = mul(float4(input.pos.xyz, 1), sk_inst[id].world).xyz;
o.pos = mul(float4(world, 1), sk_viewproj[o.view_id]);
o.world_pos = world;
float3 normal = normalize(mul(input.norm, (float3x3)sk_inst[id].world));
// flip input normals to treat backfaces like normal
o.normal = normalize(mul(-input.norm, (float3x3)sk_inst[id].world));
float3 norm_shade = float3(0.5) + (normal * 0.5);
// float3 norm_shade = float3(0.5) + (norm_color * 0.5);
float3 view_dir = normalize(world - sk_camera_pos[o.view_id]);
// o.color = norm_shaed * clamp(dot(o.normal, -view_dir), 0, 0.2);
// rim lighting
float rim = 1.0 - saturate(dot(normalize(view_dir), normal));
o.color = float4(norm_shade * rim * rim * 0.2, 1);
o.color = color * sk_inst[id].color;
return o;
}
float4 ps(psIn input) : SV_TARGET {
return input.color;
float3 view_dir = normalize(input.world_pos - input.cam_pos);
// Fresnel effect calculation
float fresnel = 1.0 - saturate(dot(-view_dir, input.normal));
fresnel = pow(fresnel, 5.0); // Adjust power for different falloff rates
float value = 0.5;
return float4(fresnel * input.color.rgb * value, input.color.a);
}

BIN
Assets/meshes/assets.glb (Stored with Git LFS)

Binary file not shown.

View file

@ -2,8 +2,8 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.dofdev.snake"
android:versionCode="11"
android:versionName="1.15"
android:versionCode="17"
android:versionName="1.27"
android:installLocation="auto"
>
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="32" />

View file

@ -49,23 +49,32 @@ todo
target audience/experience
~13-18-year-olds who enjoy competitive, skill-based, solo gaming
sprinkle in some pop culture relevant to that age demographic that's in good taste to target interests
seated experience on the Meta Quest 3/3S
moderate learning curve
start outside of the box (about 6-9 segments long)
snake is moving like normal but is parented to an offset pose that keeps their head centered
so you can easily get a feel for how the snake moves
(sort of like writhing around in zero g)
*dreamlike*
wake up in box
stylized visuals
fun to play daily for a couple weeks or so
playable with a half engaging vid or conversation in the background
start outside of the box (about 6-9 segments long)
snake is moving like normal but is parented to an offset pose that keeps their head centered
so you can easily get a feel for how the snake moves
(sort of like writhing around in zero g)
*dreamlike*
wake up in box
my tempo
on l_con tap (a) btn to step, and repeatedly to set tempo
once a tempo has been set you can stop tapping and you'll coast at that tempo
if you tap again it resets the tempo latch, so if you just tapped once it's an easy way to pause
and if you keep tapping you can control your pace and find a new tempo
long press (a) or tap (b) btn
to switch into coasting on tempo, in order to maintain the most control in ramping up and down speed with tapping without the tempo kicking in prematurely
mount/unmount box for orbital_view || handheld || desk/space view
if placed within 15 degree cone of head view then
mount (zero out relative x offset)
this makes it easier to play alongside a video, as your attention span can drift in and out
and you can take tougher parts slower
bug(s)
food.spawn fails to find a spot and defaults to zero
...
```

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Threading;
using StereoKit;
namespace snake;
@ -9,6 +10,7 @@ static class Arts
static Dictionary<string, Mesh> meshes = new();
static Material mat_mono = new Material("mono.hlsl");
static Material mat_unlit = new Material("unlit.hlsl");
static Material mat_box = new Material("unlit.hlsl");
static Material mat_backbox = new Material("backbox.hlsl");
static Material mat_justcolor = new Material("justcolor.hlsl");
@ -29,8 +31,13 @@ static class Arts
}
}
mat_backbox.Transparency = Transparency.Add;
mat_backbox.FaceCull = Cull.Front;
mat_backbox.Transparency = Transparency.Add;
mat_backbox.DepthTest = DepthTest.LessOrEq;
mat_backbox.DepthWrite = false;
mat_box.Chain = mat_backbox;
}
public static void Frame()
@ -59,13 +66,16 @@ static class Arts
// box
Hierarchy.Push(Mono.box_pose.ToMatrix(Mono.box_scale));
meshes["InsideOut"].Draw(mat_unlit, Matrix.Identity);
meshes["InsideOut"].Draw(mat_backbox, Matrix.Identity);
// meshes["InsideOut"].Draw(mat_unlit, Matrix.Identity);
meshes["InsideOut"].Draw(
mat_box,
Matrix.Identity
);
meshes["Corrugation"].Draw(
Mono.in_dist.state ? mat_justcolor : mat_unlit,
Matrix.Identity
);
if (Mono.in_cone.state)
if (Mono.in_cone.state && Mono.box_mode == Mono.BoxMode.Hold || Mono.box_mode == Mono.BoxMode.Mount)
{
meshes["Hanging"].Draw(
mat_unlit,
@ -78,14 +88,14 @@ static class Arts
meshes["uiPlay"].Draw(
mat_unlit,
Matrix.TR(
V.XYZ(0, 0, Mono.SD_Z + 0.5f + 0.1f),
V.XYZ(0, 0, Mono.SD_Z - 0.5f + 0.1f),
Quat.FromAngles(90, 0, 0)
)
);
meshes["uiCursor"].Draw(
mat_unlit,
Matrix.TR(
V.XYZ(0, 0, Mono.SD_Z + 0.5f + 0.2f),
V.XYZ(0, 0, Mono.SD_Z - 0.5f + 0.2f),
Quat.FromAngles(90, 0, 0)
)
);
@ -93,7 +103,7 @@ static class Arts
// snake
float snake_t = headmove.state ? Maths.u_clamp(Maths.smooth_stop((float)Mono.step_t) * 3.0f) : 1.0f;
bool food_next = (Mono.snake[0] + Mono.snake_dir) == Mono.food;
bool food_next = !Mono.eaten_latch.state && (Mono.snake[0] + Mono.snake_dir) == Mono.food;
if (!food_next)
{
meshes["Tongue"].Draw(
@ -149,8 +159,24 @@ static class Arts
// holes
foreach (KeyValuePair<XYZi, XYZi> hole in Mono.holes)
{
meshes["Hole"].Draw(
mat_unlit,
Vec3 hole_normal = V.XYZ(
Maths.abs(hole.Value.x) * Maths.sign(hole.Key.x),
Maths.abs(hole.Value.y) * Maths.sign(hole.Key.y),
Maths.abs(hole.Value.z) * Maths.sign(hole.Key.z)
);
// Vec3 hole_world_normal = Mono.box_pose.orientation * hole_normal;
// Vec3 hole_world = Mono.box_pose.ToMatrix(Mono.box_scale) * V.XYZ(
// hole.Key.x - hole.Value.x * 0.5f,
// hole.Key.y - hole.Value.y * 0.5f,
// hole.Key.z - hole.Value.z * 0.5f
// );
// Vec3 cam_dir = (Rig.head.orientation * Vec3.Forward);
// Vec3 hole_view_dir = Vec3.Direction(hole_world, Rig.head.position);
// bool back_hole = Vec3.Dot(hole_world_normal, hole_view_dir) < 0.0;
bool hole_flip = Vec3.Dot(hole_normal, hole.Value.ToVec3) < 0.0;
meshes[hole_flip ? "Hole" : "HoleFlip"].Draw(
mat_box,
Matrix.TRS(
hole.Key.ToVec3,
Quat.LookDir(hole.Value.ToVec3),
@ -169,13 +195,18 @@ static class Arts
10 * Time.Stepf
);
}
meshes["Food"].Draw(
mat_mono,
Matrix.TR(
Mono.food.ToVec3,
food_ori
)
);
if (!Mono.eaten_latch.state)
{
float food_t = Mono.eaten_latch.delta == -1 ? Maths.smooth_stop((float)Mono.step_t) : 1;
meshes["Food"].Draw(
mat_mono,
Matrix.TRS(
Mono.food.ToVec3,
food_ori,
food_t
)
);
}
Hierarchy.Pop();

View file

@ -13,10 +13,10 @@ static class Mono
public static Pose box_pose = new(0, -3 * U.cm, -10 * U.cm);
public static float box_scale = 1.333f * U.cm;
public const int SD_X = 2, SD_Y = 1, SD_Z = 2;
public const int SD_X = 3, SD_Y = 2, SD_Z = 3;
public static SpatialArray<int>
box_space = new(SD_X-1, SD_Y-1, SD_Z-1, -1),
s_array = new(SD_X, SD_Y, SD_Z, -1),
head_fill = new(SD_X, SD_Y, SD_Z, -1),
tail_fill = new(SD_X, SD_Y, SD_Z, -1);
public static XYZi[] snake = new XYZi[
@ -27,7 +27,8 @@ static class Mono
public static XYZi snake_dir = new(0, 0, 1);
public static DeltaBool in_box = new(true);
public static Dictionary<XYZi, XYZi> holes = new();
public static XYZi food = new(2, 0, 0);
public static XYZi food = new(2, 0, 0); // [!] start random to keep new game fresh?
public static DeltaBool eaten_latch = new(false);
public static double eat_timestamp = 0.0;
public enum BoxMode
@ -63,8 +64,11 @@ static class Mono
if (Input.Key(Key.MouseCenter).IsActive())
{
float sx = Input.Mouse.posChange.x;
float ssx = Maths.smooth_start(sx);
box_pose.orientation *= Quat.FromAngles(0, sx * 180.0f * Time.Stepf, 0);
Renderer.CameraRoot *= Matrix.R(
Quat.FromAngles(0, -sx * 180.0f * Time.Stepf, 0)
);
// orbital_view
box_pose.position = Input.Head.position + Input.Head.orientation * V.XYZ(0, -0 * U.cm, -10 * U.cm);
}
}
else
@ -102,7 +106,13 @@ static class Mono
public static void Step()
{
if (s_array[snake[0] + snake_dir] > -1)
// eat tail
if (snake[0] + snake_dir == snake[snake_len-1])
{
snake_len--;
grow_buffer = 0;
}
else if (s_array[snake[0] + snake_dir] > -1)
{
// lose condition
bool stuck = true;
@ -120,14 +130,8 @@ static class Mono
return;
}
XYZi next_pos = snake[0] + snake_dir;
XYZi inset_pos = new(
next_pos.x - Maths.sign(next_pos.x),
next_pos.y - Maths.sign(next_pos.y),
next_pos.z - Maths.sign(next_pos.z)
);
bool around_box = s_array.InRange(inset_pos);
if (!around_box)
bool in_or_around_box = s_array.InRange(snake[0] + snake_dir);
if (!in_or_around_box)
{
return;
}
@ -157,7 +161,7 @@ static class Mono
snake[0] += snake_dir;
}
in_box.Step(s_array.InRange(snake[0]));
in_box.Step(box_space.InRange(snake[0]));
if (in_box.delta != 0) // 1 just in -1 just out
{
holes.Add(snake[0], snake_dir);
@ -175,51 +179,62 @@ static class Mono
}
// eat
if (food == snake[0])
if (!eaten_latch.state)
{
eat_timestamp = Time.Total;
grow_buffer += 3;
eaten_latch.Step(food == snake[0]);
if (eaten_latch.delta == +1)
{
eat_timestamp = Time.Total;
grow_buffer += 3;
Feed();
SFX.crisp_nom.PlayBox(snake[0]);
SFX.crisp_nom.PlayBox(snake[0]);
}
} else {
(bool viable, XYZi cell) = Feed();
eaten_latch.Step(!viable);
if (eaten_latch.delta == -1)
{
food = cell;
}
}
}
static void Feed()
static (bool, XYZi) Feed()
{
head_fill.Clear(-1);
Gas(head_fill, snake[0]);
// handle out of the box exception on tail
// [!] handle out of the box exception on tail
// by making the spatial arrays encapsulate the layer outside of the box
tail_fill.Clear(-1);
Gas(tail_fill, snake[snake_len - 1]);
XYZi farthest_cell = new XYZi(0, 0, 0);
int farthest_dist = 0;
// [*] design configurable (we basically want it to be as far away as it can reasonably be)
const int max_dist = 6;
// [!] given how it loops over the space it directionally biases the results
for (int sx = -s_array.Xslen; s_array.InX(sx); sx++)
// step from head using directions towards the tail
// and stop at either 1 cell away from the tail or 5 spaces away from the head
bool viable = false;
XYZi cell = snake[0];
for (int step = 0; step < 5; step++)
{
for (int sy = -s_array.Yslen; s_array.InY(sy); sy++)
int min_dist = 100;
XYZi min_cell = new();
int[] dir_indices = GetShuffledIndices(directions);
for (int i = 0; i < directions.Length; i++)
{
for (int sz = -s_array.Zslen; s_array.InZ(sz); sz++)
XYZi dir = directions[dir_indices[i]];
XYZi dir_cell = cell + dir;
int tail_dist = tail_fill[dir_cell];
if (tail_dist > 1 && tail_dist < min_dist && box_space.InRange(dir_cell))
{
XYZi sv = new XYZi(sx, sy, sz);
int dist = head_fill[sv];
bool good_dist = dist > farthest_dist && dist < max_dist;
bool tail_access = tail_fill[sv] > 0; // help ensure completability
bool snake_free = s_array[sv] == -1;
if (good_dist && tail_access && snake_free)
{
farthest_dist = dist;
farthest_cell = sv;
}
min_dist = tail_dist;
min_cell = dir_cell;
viable = true;
}
}
cell = min_cell;
// if (min_dist <= 1)
// {
// break;
// }
}
food = farthest_cell;
return (viable, cell);
}
// space fill algorithm
@ -248,6 +263,23 @@ static class Mono
}
}
static int[] GetShuffledIndices<T>(T[] array)
{
int[] indices = new int[array.Length];
for (int i = 0; i < array.Length; i++)
{
indices[i] = i;
}
for (int i = indices.Length - 1; i > 0; i--)
{
int j = System.Random.Shared.Next(i + 1);
int temp = indices[i];
indices[i] = indices[j];
indices[j] = temp;
}
return indices;
}
// directions for moving in the grid
static readonly XYZi[] directions = new XYZi[]
{
@ -269,4 +301,14 @@ static class Mono
new XYZi(+1, +1, -1),
new XYZi(+1, +1, +1)
};
static XYZi InsetCell(XYZi cell)
{
// [!] don't inset past 0
return new(
cell.x - Maths.sign(cell.x),
cell.y - Maths.sign(cell.y),
cell.z - Maths.sign(cell.z)
);
}
}

View file

@ -39,14 +39,17 @@ static class Rig
else
{
// Hand r_hand = Input.Hand(Handed.Right);
// [!] hand input simulates controller...
Controller r_con = Input.Controller(Handed.Right);
btn_trigger.Step(r_con.trigger > 0.5f);
btn_grip.Step(r_con.grip > 0.5f);
bool con_tracked = r_con.trackedPos > TrackState.Lost;
Input.HandVisible(Handed.Max, !con_tracked);
// bool hand_tracked = Input.HandSource(Handed.Right) > HandSource.None;
Input.HandVisible(Handed.Max, false); // hide hands
if (con_tracked)
{
btn_trigger.Step(r_con.trigger > 0.5f);
btn_grip.Step(r_con.grip > 0.5f);
Vec2 stick = r_con.stick;
Quat stick_rot = Quat.FromAngles(stick.y * -90, 0, stick.x * +90);
float stick_sign = r_con.IsStickClicked ? -1 : +1;