jpskill.com
🛠️ 開発・MCP コミュニティ

hytopia-physics

HYTOPIA SDKゲームで、剛体や衝突判定、レイキャスト、力、物理演算に基づいたゲームプレイなどを実装したい場合に、PhysicsComponentやコライダーなどを活用して物理演算を組み込むのを支援するSkill。

📜 元の英語説明(参考)

Helps implement physics and collision in HYTOPIA SDK games. Use when users need rigid bodies, collision detection, raycasting, forces, or physics-based gameplay. Covers PhysicsComponent, colliders, raycasting, and physics simulation.

🇯🇵 日本人クリエイター向け解説

一言でいうと

HYTOPIA SDKゲームで、剛体や衝突判定、レイキャスト、力、物理演算に基づいたゲームプレイなどを実装したい場合に、PhysicsComponentやコライダーなどを活用して物理演算を組み込むのを支援するSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o hytopia-physics.zip https://jpskill.com/download/9066.zip && unzip -o hytopia-physics.zip && rm hytopia-physics.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/9066.zip -OutFile "$d\hytopia-physics.zip"; Expand-Archive "$d\hytopia-physics.zip" -DestinationPath $d -Force; ri "$d\hytopia-physics.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して hytopia-physics.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → hytopia-physics フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

HYTOPIA Physics & Collision

このスキルは、HYTOPIA SDK ゲームで物理演算と衝突判定を実装するのに役立ちます。

このスキルを使用する場面

このスキルは、ユーザーが以下のようなことをしたい場合に利用します。

  • エンティティに物理演算(重力、速度、力)を追加したい
  • オブジェクト間の衝突判定が必要
  • ヒット検出のためのレイキャスティングについて知りたい
  • 物理演算に基づいたゲームプレイ(投射物、爆発)を作成したい
  • コライダー(ボックス、球、メッシュ)を設定する必要がある
  • 物理マテリアル(摩擦、反発力)について知りたい

コアとなる物理演算の概念

エンティティへの物理演算の追加

import { Entity, PhysicsComponent, BoxCollider } from 'hytopia';

class PhysicsEntity extends Entity {
  constructor() {
    super();

    this.addComponent(new PhysicsComponent({
      mass: 1.0,           // キログラム
      gravity: { x: 0, y: -9.81, z: 0 },
      linearDamping: 0.1,  // 空気抵抗
      angularDamping: 0.1,
      useGravity: true
    }));

    // 衝突形状を追加
    this.addComponent(new BoxCollider({
      size: { x: 1, y: 1, z: 1 },
      offset: { x: 0, y: 0.5, z: 0 }
    }));
  }
}

コライダーの種類

import { BoxCollider, SphereCollider, MeshCollider } from 'hytopia';

// ボックスコライダー
const box = new BoxCollider({
  size: { x: 2, y: 1, z: 0.5 },
  offset: { x: 0, y: 0, z: 0 }
});

// 球コライダー
const sphere = new SphereCollider({
  radius: 0.5,
  offset: { x: 0, y: 0.5, z: 0 }
});

// メッシュコライダー(モデルから)
const mesh = new MeshCollider({
  modelUri: 'models/terrain.gltf',
  convex: false  // false = 正確なメッシュ, true = 凸包 (より高速)
});

力の適用

import { Entity, PhysicsComponent } from 'hytopia';

class Projectile extends Entity {
  physics: PhysicsComponent;

  constructor() {
    super();
    this.physics = new PhysicsComponent({
      mass: 0.1,
      useGravity: true
    });
    this.addComponent(this.physics);
  }

  launch(direction: Vector3, force: number) {
    // インパルス(瞬間的な力)を適用
    this.physics.applyImpulse(direction.multiply(force));

    // または、継続的な力を適用
    this.physics.applyForce(direction.multiply(force));

    // トルク(回転)を適用
    this.physics.applyTorque({ x: 0, y: 100, z: 0 });
  }
}

レイキャスティング

基本的なレイキャスト

import { World } from 'hytopia';

// ある点からある方向へのレイキャスト
const result = world.raycast(
  { x: 0, y: 10, z: 0 },     // 原点
  { x: 0, y: -1, z: 0 },     // 方向 (正規化済み)
  100                         // 最大距離
);

if (result.hit) {
  console.log('Hit at:', result.position);
  console.log('Hit entity:', result.entity);
  console.log('Hit normal:', result.normal);
  console.log('Hit distance:', result.distance);
}

プレイヤーの視線レイキャスト

// プレイヤーは何を見ているか?
const raycast = world.raycast(
  player.position,
  player.lookDirection,
  5  // リーチ距離
);

if (raycast.hit) {
  if (raycast.block) {
    // ブロックを見ている
    console.log('Block:', raycast.block.type);
  }
  if (raycast.entity) {
    // エンティティを見ている
    console.log('Entity:', raycast.entity.id);
  }
}

衝突判定

衝突イベント

import { Entity, CollisionComponent } from 'hytopia';

class CollidableEntity extends Entity {
  constructor() {
    super();

    const collision = new CollisionComponent();

    collision.onCollisionEnter = (other) => {
      console.log('Started colliding with:', other.id);

      if (other instanceof Projectile) {
        this.takeDamage(10);
      }
    };

    collision.onCollisionExit = (other) => {
      console.log('Stopped colliding with:', other.id);
    };

    collision.onCollisionStay = (other) => {
      // 衝突中に毎フレーム呼び出される
    };

    this.addComponent(collision);
  }
}

衝突レイヤー

import { PhysicsComponent, CollisionLayer } from 'hytopia';

// 何と何が衝突するかを定義
const physics = new PhysicsComponent({
  mass: 1,
  collisionLayer: CollisionLayer.DEFAULT,
  collisionMask: CollisionLayer.DEFAULT | CollisionLayer.PLAYER
});

// レイヤー: DEFAULT, PLAYER, ENEMY, PROJECTILE, TRIGGER, etc.
// プレイヤーは他のプレイヤーとは衝突しないが、敵とは衝突する
const playerPhysics = new PhysicsComponent({
  collisionLayer: CollisionLayer.PLAYER,
  collisionMask: CollisionLayer.DEFAULT | CollisionLayer.ENEMY | CollisionLayer.PROJECTILE
});

物理マテリアル

import { PhysicsMaterial } from 'hytopia';

const bouncyMaterial = new PhysicsMaterial({
  friction: 0.5,        // 0 = 滑りやすい, 1 = 粗い
  restitution: 0.8,     // 0 = 反発しない, 1 = 完全に反発する
  density: 1.0          // 質量計算に影響
});

const iceMaterial = new PhysicsMaterial({
  friction: 0.1,
  restitution: 0.1
});

// コライダーに適用
const collider = new BoxCollider({
  size: { x: 1, y: 1, z: 1 },
  material: bouncyMaterial
});

ベストプラクティス

  1. シンプルなコライダーを使用する - Box/Sphere は Mesh よりも高速です
  2. 適切な質量を設定する - 現実的な値 (kg) が最適です
  3. レイヤーを賢く使用する - 衝突をフィルタリングしてパフォーマンスを向上させます
  4. スポーン前にレイキャストする - スペースが空いているか確認します
  5. 非アクティブなボディをスリープさせる - 静止オブジェクトの CPU を節約します

一般的なパターン

接地判定

function isGrounded(entity: Entity): boolean {
  const raycast = world.raycast(
    entity.position,
    { x: 0, y: -1, z: 0 },
    0.1  // エンティティの真下の小さな距離
  );
  return raycast.hit && raycast.distance < 0.05;
}

// 使用例
if (isGrounded(player) && input.isPressed('space')) {
  player.physics.applyImpulse({ x: 0, y: 10, z: 0 });
}

爆発力

function applyExplosion(center: Vector3, radius: number, force: number) {
  // 半径内のすべてのエンティティを取得
  const entities = world.getEntitiesInRadius(center, radius);

  for (const entity of entities) {
    const direction = entity.position.subtract(center).normalize();
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

HYTOPIA Physics & Collision

This skill helps you implement physics and collision in HYTOPIA SDK games.

When to Use This Skill

Use this skill when the user:

  • Wants to add physics to entities (gravity, velocity, forces)
  • Needs collision detection between objects
  • Asks about raycasting for hit detection
  • Wants to create physics-based gameplay (projectiles, explosions)
  • Needs to configure colliders (boxes, spheres, meshes)
  • Asks about physics materials (friction, bounciness)

Core Physics Concepts

Adding Physics to Entity

import { Entity, PhysicsComponent, BoxCollider } from 'hytopia';

class PhysicsEntity extends Entity {
  constructor() {
    super();

    this.addComponent(new PhysicsComponent({
      mass: 1.0,           // Kilograms
      gravity: { x: 0, y: -9.81, z: 0 },
      linearDamping: 0.1,  // Air resistance
      angularDamping: 0.1,
      useGravity: true
    }));

    // Add collision shape
    this.addComponent(new BoxCollider({
      size: { x: 1, y: 1, z: 1 },
      offset: { x: 0, y: 0.5, z: 0 }
    }));
  }
}

Collider Types

import { BoxCollider, SphereCollider, MeshCollider } from 'hytopia';

// Box collider
const box = new BoxCollider({
  size: { x: 2, y: 1, z: 0.5 },
  offset: { x: 0, y: 0, z: 0 }
});

// Sphere collider
const sphere = new SphereCollider({
  radius: 0.5,
  offset: { x: 0, y: 0.5, z: 0 }
});

// Mesh collider (from model)
const mesh = new MeshCollider({
  modelUri: 'models/terrain.gltf',
  convex: false  // false = exact mesh, true = convex hull (faster)
});

Applying Forces

import { Entity, PhysicsComponent } from 'hytopia';

class Projectile extends Entity {
  physics: PhysicsComponent;

  constructor() {
    super();
    this.physics = new PhysicsComponent({
      mass: 0.1,
      useGravity: true
    });
    this.addComponent(this.physics);
  }

  launch(direction: Vector3, force: number) {
    // Apply impulse (instant force)
    this.physics.applyImpulse(direction.multiply(force));

    // Or apply continuous force
    this.physics.applyForce(direction.multiply(force));

    // Apply torque (rotation)
    this.physics.applyTorque({ x: 0, y: 100, z: 0 });
  }
}

Raycasting

Basic Raycast

import { World } from 'hytopia';

// Raycast from point in direction
const result = world.raycast(
  { x: 0, y: 10, z: 0 },     // Origin
  { x: 0, y: -1, z: 0 },     // Direction (normalized)
  100                         // Max distance
);

if (result.hit) {
  console.log('Hit at:', result.position);
  console.log('Hit entity:', result.entity);
  console.log('Hit normal:', result.normal);
  console.log('Hit distance:', result.distance);
}

Player Look Raycast

// What is player looking at?
const raycast = world.raycast(
  player.position,
  player.lookDirection,
  5  // Reach distance
);

if (raycast.hit) {
  if (raycast.block) {
    // Looking at a block
    console.log('Block:', raycast.block.type);
  }
  if (raycast.entity) {
    // Looking at an entity
    console.log('Entity:', raycast.entity.id);
  }
}

Collision Detection

Collision Events

import { Entity, CollisionComponent } from 'hytopia';

class CollidableEntity extends Entity {
  constructor() {
    super();

    const collision = new CollisionComponent();

    collision.onCollisionEnter = (other) => {
      console.log('Started colliding with:', other.id);

      if (other instanceof Projectile) {
        this.takeDamage(10);
      }
    };

    collision.onCollisionExit = (other) => {
      console.log('Stopped colliding with:', other.id);
    };

    collision.onCollisionStay = (other) => {
      // Called every frame while colliding
    };

    this.addComponent(collision);
  }
}

Collision Layers

import { PhysicsComponent, CollisionLayer } from 'hytopia';

// Define what collides with what
const physics = new PhysicsComponent({
  mass: 1,
  collisionLayer: CollisionLayer.DEFAULT,
  collisionMask: CollisionLayer.DEFAULT | CollisionLayer.PLAYER
});

// Layers: DEFAULT, PLAYER, ENEMY, PROJECTILE, TRIGGER, etc.
// Player doesn't collide with other players but collides with enemies
const playerPhysics = new PhysicsComponent({
  collisionLayer: CollisionLayer.PLAYER,
  collisionMask: CollisionLayer.DEFAULT | CollisionLayer.ENEMY | CollisionLayer.PROJECTILE
});

Physics Materials

import { PhysicsMaterial } from 'hytopia';

const bouncyMaterial = new PhysicsMaterial({
  friction: 0.5,        // 0 = slippery, 1 = rough
  restitution: 0.8,     // 0 = no bounce, 1 = perfect bounce
  density: 1.0          // Affects mass calculation
});

const iceMaterial = new PhysicsMaterial({
  friction: 0.1,
  restitution: 0.1
});

// Apply to collider
const collider = new BoxCollider({
  size: { x: 1, y: 1, z: 1 },
  material: bouncyMaterial
});

Best Practices

  1. Use simple colliders - Box/Sphere are faster than Mesh
  2. Set appropriate mass - Realistic values (kg) work best
  3. Use layers wisely - Filter collisions to improve performance
  4. Raycast before spawning - Check if space is clear
  5. Sleep inactive bodies - Saves CPU for stationary objects

Common Patterns

Ground Check

function isGrounded(entity: Entity): boolean {
  const raycast = world.raycast(
    entity.position,
    { x: 0, y: -1, z: 0 },
    0.1  // Small distance below entity
  );
  return raycast.hit && raycast.distance < 0.05;
}

// Usage
if (isGrounded(player) && input.isPressed('space')) {
  player.physics.applyImpulse({ x: 0, y: 10, z: 0 });
}

Explosion Force

function applyExplosion(center: Vector3, radius: number, force: number) {
  // Get all entities in radius
  const entities = world.getEntitiesInRadius(center, radius);

  for (const entity of entities) {
    const direction = entity.position.subtract(center).normalize();
    const distance = entity.position.distance(center);
    const falloff = 1 - (distance / radius);  // Stronger closer to center

    if (entity.physics) {
      entity.physics.applyImpulse(
        direction.multiply(force * falloff)
      );
    }
  }
}