2024年7月22日月曜日

Spatial Mini Games Privacy Policy

 Our app does not collect, store, or share any personal information from users.

Data Not Collected

  • Contact Information (name, email address, etc.)
  • Health/Fitness Information
  • Financial Information
  • Location Data
  • Sensitive Information
  • User Content (photos, videos, audio data, etc.)
  • Browsing/Search History
  • Identifiers (user ID, device ID)
  • Usage Data
  • Diagnostic Data
  • Environmental Scanning Data
  • Physical Data

Our app does not collect data on hand movements, gaze positions, or any other gameplay-related data.


Spatial Mini Games

https://apps.apple.com/us/app/spatial-mini-games/id6553981220

Support mail:junichistamesi@gmail.com

Privacy Policy

Overview:

 Spatial Mini Games is a collection of engaging and interactive spatial experiences designed for Apple Vision Pro. Utilize the power of visionOS to immerse yourself in various fun and challenging mini-games that will test your reflexes and spatial awareness. Perfect for casual play, each game is quick and accessible, ideal for solo play or friendly fun at home gatherings.


Game Rules:

Eye Laser Blast: Look at glass bottles on walls and ceilings, then pinch your fingers to shoot lasers from your eyes and break them. Score points by breaking bottles within 1 minute. Pinch while looking to the right side of your field of vision to shoot a red laser, and pinch while looking to the left side of your field of vision to shoot a blue laser. Red bottles break with red lasers, blue bottles break with blue lasers, and green bottles break with either. Lasers reflect off real-world surfaces.

Finger Shooter: Move your thumb near your middle finger's second joint to shoot a ball from your index finger. Hit pumpkins to score points within 1 minute. Red balls come from the right hand, blue balls from the left. Red pumpkins need red balls, blue pumpkins need blue balls, and green pumpkins can be hit with either. Balls reflect off real-world surfaces.

Gaze Walker: Control a stick figure character with your gaze to collect coins while avoiding enemy robots. Utilize real-world objects for cover and hide behind them to evade enemies. Collect as many coins as possible within 1 minute.

Overall Features:

Interactive Environments: Balls and lasers interact with real-world surfaces, enhancing the immersive experience.
Use real-world objects strategically in Gaze Walker, hiding behind them to evade enemy robots.

Quick and Casual Play: Enjoy games that last just 1 minute, perfect for short play sessions.

Convenient Controls: Access menus anytime using the left-hand menu in each mini game to toggle music or return to the game selection screen.




Feature Details and Instructions
  • Safety Notice Confirmation: The initial safety notice is confirmed by gazing at and tapping three checkboxes.
  • Game Selection Screen:
    • Tap on a bubble to display the game start button, game description, and up to the top 10 previous scores.
    • Tap the config button to access the music toggle button and the reset game data button.
  • Game Start Transition:
    • After selecting a game, a "Mini Games" panel is displayed, which changes to the Game Start button after 3 seconds.
  • Wall Scanning:
    • The selected game begins with a scanned representation of the actual wall.
    • It is recommended to look around and ensure the wall is correctly scanned.
  • Permission Request:
    • Upon first use, the app will ask for permission to access hand movement and environmental data. You must select "Yes" to proceed with the game.
    • If you accidentally select "No," go to settings, select the app, and allow access to "Hand Pose and Motion" and "Surroundings" for Spatial Mini Games.
  • In-Game Configuration:
    • During any mini-game, tap the config button on your left wrist to display the music toggle button, home button, and remaining time.

  • 2024年5月28日火曜日

    How to transfer root bone pose animation to the animation of the entire armature Old

    #RootPoseToArmature

    import bpy

    from mathutils import Matrix, Vector


    # Note: This script will automatically set the armature's rotation mode to QUATERNION.

    # The root bone will be deleted after the script runs.

    # Make sure to back up your project before running this script.


    # Set the name of the root bone

    root_bone_name = "Root"  # Set the name of the root bone


    # Get the currently selected object

    armature = bpy.context.active_object

    if not armature or armature.type != 'ARMATURE':

        raise ValueError("Please select an armature object.")


    bpy.context.view_layer.objects.active = armature


    # Switch to Object Mode to get the default pose

    bpy.ops.object.mode_set(mode='OBJECT')


    # Get the default global location and rotation of the root bone

    root_bone = armature.pose.bones.get(root_bone_name)

    if not root_bone:

        raise ValueError(f"Root bone '{root_bone_name}' not found.")


    # Get the armature's rest pose

    armature_matrix_world = armature.matrix_world.copy()

    root_bone_matrix_rest = armature.data.bones[root_bone_name].matrix_local.copy()

    root_bone_matrix_world_rest = armature_matrix_world @ root_bone_matrix_rest

    root_bone_default_loc, root_bone_default_rot, _ = root_bone_matrix_world_rest.decompose()

    inverse_root_bone_default_rot = root_bone_default_rot.inverted()


    # Get the IK targets

    ik_targets = []

    for bone in armature.pose.bones:

        for constraint in bone.constraints:

            if constraint.type == 'IK' and constraint.target:

                target = constraint.target

                if target.parent == armature:

                    ik_targets.append(target)


    # Record the global positions of the IK targets for each keyframe

    ik_target_keyframes = {}

    for target in ik_targets:

        ik_target_keyframes[target.name] = {}

        if target.animation_data and target.animation_data.action:

            for fcurve in target.animation_data.action.fcurves:

                for keyframe in fcurve.keyframe_points:

                    frame = int(keyframe.co.x)

                    bpy.context.scene.frame_set(frame)

                    ik_target_keyframes[target.name][frame] = target.matrix_world.translation.copy()


    # Get the animation data

    animation_data = armature.animation_data

    if not animation_data:

        raise ValueError("Animation data not found.")


    action = animation_data.action

    if not action:

        raise ValueError("Action not found in animation data.")


    # Process the animation data and apply the root bone transform to the armature

    transform_data = {'location': [], 'rotation_quaternion': [], 'scale': []}

    for fcurve in action.fcurves:

        if fcurve.data_path.startswith(f"pose.bones[\"{root_bone_name}\"]"):

            for keyframe in fcurve.keyframe_points:

                frame = int(keyframe.co.x)


                # Move to the frame to set the keyframe

                bpy.context.scene.frame_set(frame)


                # Get the pose position of the root bone

                root_bone_matrix = root_bone.matrix


                # Calculate the global transform of the root bone

                global_matrix = armature.matrix_world @ root_bone_matrix


                # Decompose the matrix into location, rotation, and scale

                global_loc, global_rot, global_scale = global_matrix.decompose()


                # Save the transform data in the dictionary

                transform_data['location'].append((frame, global_loc))

                transform_data['rotation_quaternion'].append((frame, global_rot))

                transform_data['scale'].append((frame, global_scale))


    # Switch to Object Mode to apply transformations

    bpy.ops.object.mode_set(mode='OBJECT')


    # Automatically set the armature's rotation mode to QUATERNION

    armature.rotation_mode = 'QUATERNION'


    # Apply the transforms to the armature object and insert keyframes

    for (frame, loc), (_, rot) in zip(transform_data['location'], transform_data['rotation_quaternion']):

        # Correct the rotation for the root bone's default rotation

        corrected_rot = (rot @ inverse_root_bone_default_rot)

        

        # Create the translation matrices for the default and current locations

        translation_matrix_to_default = Matrix.Translation(-root_bone_default_loc)

        translation_matrix_back = Matrix.Translation(root_bone_default_loc)

        translation_matrix_current = Matrix.Translation(loc)

        

        # Create the rotation matrix for the corrected rotation

        rotation_matrix = corrected_rot.to_matrix().to_4x4()

        

        # Combine the matrices: 

        # 1. Translate to default position

        # 2. Apply rotation

        # 3. Translate to the current location

        final_matrix = translation_matrix_current @ rotation_matrix @ translation_matrix_to_default

        

        # Decompose the final matrix into location and rotation

        final_loc, final_rot, _ = final_matrix.decompose()

        

        # Apply the final location and rotation to the armature

        armature.location = final_loc

        armature.rotation_quaternion = final_rot

        armature.keyframe_insert(data_path="location", frame=frame)

        armature.keyframe_insert(data_path="rotation_quaternion", frame=frame)


    for frame, scale in transform_data['scale']:

        armature.scale = scale

        armature.keyframe_insert(data_path="scale", frame=frame)


    # Update the IK target positions for each frame

    for target in ik_targets:

        for frame, initial_position in ik_target_keyframes[target.name].items():

            bpy.context.scene.frame_set(frame)

            # Convert the initial position to the armature's local space at the current frame

            local_pos = armature.matrix_world.inverted() @ initial_position

            target.location = local_pos

            target.keyframe_insert(data_path="location", frame=frame)


    # Delete the root bone

    bpy.ops.object.mode_set(mode='EDIT')

    bones = armature.data.edit_bones

    root_bone = bones.get(root_bone_name)

    if root_bone:

        bones.remove(root_bone)


    # Restore the current frame

    bpy.context.scene.frame_set(bpy.context.scene.frame_current)


    2024年4月30日火曜日

    UnityでつけたポーズをBlenderに取り込む方法(もっと簡単な方法あるかも)

    Unityのエディタでシーンビュー内でつけたポーズをBlenderにもっていく方法
    1. Unity側のスクリプトをポーズを付けたFBXにアタッチしてレストポーズにしたFBXを
    referenceModelとして設定する。
    ポーズを付けたFBXのrootBoneと出力先のパスを設定する。
    2. インスペクター上で右クリックでExportBoneData
    3. Blender側のスクリプトでアーマチュアの名前と読み込むポーズのファイルのパスを設定して RunScript


    ↓Unity側

    using System.Collections.Generic;

    using UnityEngine;

    using System.IO;


    [System.Serializable]

    public class BoneData

    {

        public string name;

        public Vector3 position;

        public Vector3 scale;

        public Quaternion rotation; // This will be Q_relative

    }


    [System.Serializable]

    public class BoneDataList

    {

        public List<BoneData> bones = new List<BoneData>();

    }


    public class BoneDataExporter : MonoBehaviour

    {

        public Transform rootBone;

        public GameObject referenceModel; // Add a field for the reference model

        public string boneDataPath = "Assets/boneData.json";



        [ContextMenu("Export Bone Data")]

        void ExportBoneData()

        {

            BoneDataList boneDataList = new BoneDataList();

            CollectBoneData(rootBone, boneDataList.bones, referenceModel.transform);

            string json = JsonUtility.ToJson(boneDataList, true);

            File.WriteAllText(boneDataPath, json);

            Debug.Log("Bone data exported!");

        }


        void CollectBoneData(Transform bone, List<BoneData> boneData, Transform reference)

        {


            // Find the same bone in the reference model by name

            Transform referenceBone = reference.FindChildRecursive(bone.name);

            Quaternion Q0 = referenceBone != null ? referenceBone.localRotation : Quaternion.identity;

            Quaternion Q = bone.localRotation;

            Quaternion Q0_inv= Quaternion.Inverse(Q0);

            Quaternion Q_relative =Q0_inv*Q;

            Quaternion Q_final = new Quaternion(Q_relative.x, -Q_relative.y, -Q_relative.z, Q_relative.w);


            boneData.Add(new BoneData

            {

                name = bone.name,

                position = bone.localPosition,

                scale = bone.localScale,

                rotation = Q_final // Store the relative rotation

            });


            foreach (Transform child in bone)

            {

                CollectBoneData(child, boneData, reference);

            }

        }

    }


    ↓Blender側

    import bpy

    import json


    def apply_pose(armature_name, json_path):

        with open(json_path, 'r') as f:

            data = json.load(f)


        armature = bpy.data.objects[armature_name]

        bpy.context.view_layer.objects.active = armature

        bpy.ops.object.mode_set(mode='POSE')


        for bone_data in data['bones']:

            bone = armature.pose.bones.get(bone_data['name'])

            if bone:

                bone.location = (bone_data['position']['x'], bone_data['position']['y'], bone_data['position']['z'])

                bone.scale = (bone_data['scale']['x'], bone_data['scale']['y'], bone_data['scale']['z'])

                # 注意:四元数は (w, x, y, z) の順で設定

                bone.rotation_quaternion = (bone_data['rotation']['w'], bone_data['rotation']['x'], bone_data['rotation']['y'], bone_data['rotation']['z'])


        # Save the pose

        bpy.ops.object.posemode_toggle()

        bpy.ops.wm.save_mainfile()


    # ファイルパスは適宜設定してください


    apply_pose('RobinArmature', 'C:boneData.json')

        





    2024年4月22日月曜日

    Efficient Use of Blender Assets

    -How to bake actions in Blender

    http://eizouasobi.blogspot.com/2024/04/how-to-bake-actions-in-blender.html


    -How to split actions in Blender

    http://eizouasobi.blogspot.com/2024/04/how-to-split-actions-in-blender.html


    -How to use in game engine

    https://eizouasobi.blogspot.com/2019/01/how-to-use-in-game-engine.html

    -How to change the scale
    http://eizouasobi.blogspot.com/2019/01/how-to-change-scale.html

    Formatting Animation Name Files for Use with ActionMaking.py

    If the animation number files attached to your assets are not in the correct format for use with the ActionMaking.py script, please use the FormatAnimationNamesAnimColonNum.py script to adjust them.


    This script performs the following tasks:

    -Removes empty lines and trailing colons: This step cleans up unnecessary formatting that may interfere with processing.

    -Replaces spaces with colons: This adjustment is made for lines where the frame range and animation name are separated by spaces instead of colons, ensuring consistency in data format.

    -Checks and adjusts the position of name and frame data: This part verifies whether the line format is 'frames:name' or 'name:frames' and adjusts accordingly.

    -Processes frame data: Depending on the number of listed frame ranges, the script selects the appropriate frames (the middle ones if there are four frames, the first two if there are three).

    For example, a file with lines like:

    50-80-110-140:WalkForward:

    IdleToTrot:3850-3880-3910

    will be changed to:

    WalkForward:80-110

    IdleToTrot:3850-3880

    If there are any extraneous comments or notes that cannot be adjusted by this script, please remove them before running the script.

    2024年4月20日土曜日

    How to split actions in Blender

    One of the ways to further utilize animation assets in Blender is to split actions. Here is a step-by-step guide on how to split actions.


    Step 1: Baking the Animation

    First, bake the animation in Blender. This involves solidifying the animation data and making any necessary adjustments such as removing root motion. For detailed instructions, please refer to this link.


    Step 2: Preparing a Text File for Action Splitting

    Prepare a text file in the following format to split actions:

    WalkForward:10-40

    WalkRight:50-80

    If the format of the animation names in the text file differs (e.g., "10-40 WalkForward"), please adjust the format using the script provided at this link.


    Step 3: Using the ActionMakingV5.py Script

    Open the  script from Blender’s text editor. Set the animation_data to text prepared in Step 2, open the NLA editor then push down action and set the strip.name to the name of the strip you want to split.

    Step 4: Running the Script

    Select the target armature and run the Run Script. This will appropriately split the specified action based on the text, making each action available as a separate item.

    ActionMakingV5.py

    import bpy


    def split_action_strip_manual_steps(animation_data):

        """

        Splits an NLA strip named "Action" into multiple strips by following the exact manual steps:

        1. Select frame range

        2. Bake with VisualKeying=True and OnlySelectedBones=False

        3. Push Down Action to create a new strip

        4. Rename the strip

        

        Makes sure to prioritize the Action strip by moving its track to the top each iteration.

        

        Args:

            animation_data: A string of animation segments in format "Name:start-end"

                            with each segment on a new line

        """

        # Parse the animation data

        animations = []

        for line in animation_data.strip().split('\n'):

            if line.strip():

                name, frame_range = line.split(':')

                start, end = map(int, frame_range.split('-'))

                animations.append((name, start, end))

        

        # Sort animations by start frame

        animations.sort(key=lambda x: x[1])

        

        # Get the active object (should be an armature)

        obj = bpy.context.active_object

        if not obj or obj.type != 'ARMATURE':

            raise Exception("Please select an armature object")

        

        # Make sure we have animation data

        if not obj.animation_data:

            raise Exception("The selected armature has no animation data")

        

        # Find the action strip in the NLA editor

        action_strip = None

        action_track = None

        

        for track in obj.animation_data.nla_tracks:

            for strip in track.strips:

                if strip.name == "Action":

                    action_strip = strip

                    action_track = track

                    break

            if action_strip:

                break

        

        if not action_strip:

            raise Exception("Could not find a strip named 'Action' in the NLA editor")

        

        # Store the original action

        original_action = action_strip.action

        if not original_action:

            raise Exception("The Action strip does not have an action assigned")

        

        # Store original frame settings

        original_frame = bpy.context.scene.frame_current

        

        # Clear any active action

        stored_action = obj.animation_data.action

        obj.animation_data.action = None

        

        # Store the original strip settings to restore later

        original_strip_start = action_strip.frame_start

        original_strip_end = action_strip.frame_end

        original_action_start = action_strip.action_frame_start

        original_action_end = action_strip.action_frame_end

        

        # Find the NLA Editor area (we'll need this for several operations)

        nla_editor = None

        for area in bpy.context.screen.areas:

            if area.type == 'NLA_EDITOR':

                nla_editor = area

                break

        

        if not nla_editor:

            raise Exception("Please open an NLA Editor window")

        

        # Create a context override to operate in the NLA editor

        override = {'area': nla_editor, 'region': nla_editor.regions[-1]}

        

        # Create a list to store the created strips for cleanup

        created_strips = []

        

        # Process each animation segment

        for i, (name, start, end) in enumerate(animations):

            print(f"\nProcessing {name} ({start}-{end})...")

            

            # IMPORTANT: Move the original action track to the top of the stack

            # This ensures it has priority in the animation evaluation

            if i > 0:  # We don't need to do this for the first iteration

                # Get the current index of the action track

                action_track_index = list(obj.animation_data.nla_tracks).index(action_track)

                

                # Move the action track to the top

                for _ in range(action_track_index):

                    with bpy.context.temp_override(**override):

                        action_track.select = True

                        bpy.ops.nla.tracks_move(direction='UP')

            

            # STEP 1: Select frame range by modifying the action strip

            action_strip.frame_start = start

            action_strip.frame_end = end

            action_strip.action_frame_start = start

            action_strip.action_frame_end = end

            

            # Make sure we're in pose mode

            if obj.mode != 'POSE':

                bpy.ops.object.mode_set(mode='POSE')

            

            # Set the current frame to the start of the range

            bpy.context.scene.frame_set(start)

            

            # STEP 2: Bake the current action strip

            

            # Make sure only the action strip is mutable, mute all other strips

            # This ensures only our target animation is evaluated during baking

            for track in obj.animation_data.nla_tracks:

                for strip in track.strips:

                    if strip != action_strip:

                        strip.mute = True

            

            # Deselect all strips

            with bpy.context.temp_override(**override):

                bpy.ops.nla.select_all(action='DESELECT')

            

            # Select our action strip

            action_strip.select = True

            

            # Make sure the NLA is enabled for evaluation

            obj.animation_data.use_nla = True

            

            # Bake the animation with the specified settings

            with bpy.context.temp_override(**override):

                bpy.ops.nla.bake(

                    frame_start=start,

                    frame_end=end,

                    step=1,

                    only_selected=False,

                    visual_keying=True,

                    clear_constraints=False,

                    clear_parents=False,

                    use_current_action=True,

                    bake_types={'POSE'}

                )

            

            # STEP 3: Push Down Action to create a strip

            # At this point, the bake operation would have created a new active action

            baked_action = obj.animation_data.action

            if not baked_action:

                raise Exception(f"Baking failed for {name}, no action was created")

            

            # Rename the baked action to match our segment name

            baked_action.name = name

            

            # Push down the action to create a strip

            with bpy.context.temp_override(**override):

                bpy.ops.nla.actionclip_add(action=baked_action.name)

            

            # Find the newly created strip (should be the latest one added)

            new_track = None

            new_strip = None

            

            # Look for the new strip in all tracks

            for track in obj.animation_data.nla_tracks:

                for strip in track.strips:

                    if strip.action == baked_action and strip != action_strip:

                        new_track = track

                        new_strip = strip

                        break

                if new_strip:

                    break

            

            if not new_strip:

                print(f"Warning: Could not find the new strip for {name}, creating it manually")

                # Create a new track and strip manually as a fallback

                new_track = obj.animation_data.nla_tracks.new()

                new_track.name = name

                new_strip = new_track.strips.new(name=name, start=start, action=baked_action)

                new_strip.frame_start = start

                new_strip.frame_end = end

            

            # STEP 4: Rename the strip

            new_strip.name = name

            if new_track:

                new_track.name = "SplitActions"

            

            # Add to our list of created strips

            created_strips.append((new_track, new_strip))

            

            print(f"Created strip: {new_strip.name} with action: {baked_action.name}")

            

            # Reset for the next iteration

            obj.animation_data.action = None

            

            # Restore the original action strip settings for the next iteration

            action_strip.frame_start = original_strip_start

            action_strip.frame_end = original_strip_end

            action_strip.action_frame_start = original_action_start

            action_strip.action_frame_end = original_action_end

            

            # Unmute the strip we just created

            new_strip.mute = False

            

            # Mute all other strips except the action strip for the next iteration

            action_strip.mute = False

        

        # Cleanup - unmute all strips

        for track in obj.animation_data.nla_tracks:

            for strip in track.strips:

                strip.mute = False

        

        # Restore the original frame

        bpy.context.scene.frame_set(original_frame)

        

        # Restore original active action if there was one

        if stored_action:

            obj.animation_data.action = stored_action

        

        print("\nAnimation split complete!")

        print("Created action strips:")

        for anim in animations:

            print(f"- {anim[0]} (frames {anim[1]}-{anim[2]})")

        

        return True


    # Example usage

    animation_data = """Idle1:10-30

    IdleToSoar:40-75

    FlapForward:100-130

    GlideForward:140-170

    GlideRight:180-210

    GlideLeft:220-250"""


    # Run the function

    try:

        split_action_strip_manual_steps(animation_data)

    except Exception as e:

        print(f"Error: {str(e)}")