Game Framework
Examples

Interactive Rotating Cube Demo

An interactive 3D rotating cube demo showcasing bidirectional Flutter-Unity communication with a beautiful overlay UI. Perfect as your first Game Framework example!

Overview

This example demonstrates:

  • Real-time parameter control from Flutter
  • Continuous feedback from Unity to Flutter
  • Touch-based interaction in Unity
  • UI overlay without blocking the Unity view
  • Color customization and state synchronization

This is the perfect starting point for learning Game Framework. It covers all the essential communication patterns in a simple, visual way.

Features

Unity Side

  • Procedurally generated 3D cube with metallic material
  • Real-time rotation with customizable speed and axis
  • Physics-based touch controls with momentum
  • Live UI display showing:
    • Speed (degrees/second and RPM)
    • Last message received
    • Communication direction indicator
  • Color-coded indicators:
    • Green: ← FROM FLUTTER
    • Orange: → TO FLUTTER
  • Dynamic lighting for visual appeal

Flutter Side

  • Beautiful overlay UI that doesn't block Unity view
  • Speed slider (-180° to 180°/second) with live preview
  • Axis selector buttons (X, Y, Z, All)
  • Action buttons:
    • Reset: Restore defaults
    • Get State: Request current state
    • Random Color: Apply random cube color
  • Live info card showing real-time stats
  • Dark theme with semi-transparent cards

Setup

1. Sync Template Scripts

cd your_flutter_project
game sync scripts --templates

2. Unity Setup

In Unity Editor:

  1. Create a new scene (or use existing)
  2. Create an empty GameObject
  3. Name it GameFrameworkDemo
  4. Attach the GameFrameworkDemo.cs script
  5. Press Play to test

The script automatically creates:

  • The rotating cube with material
  • Camera positioned to view the cube
  • Directional light for proper lighting
  • Canvas with TextMeshPro UI elements

No manual setup required! The script creates everything it needs at runtime.

3. Export Unity

# Export for your target platform
game export unity --platform android

# Sync to Flutter
game sync unity --platform android

Flutter Integration

Basic Implementation

import 'package:flutter/material.dart';
import 'package:gameframework/gameframework.dart';
import 'package:gameframework_unity/gameframework_unity.dart';

class RotatingCubeDemo extends StatefulWidget {
  @override
  State<RotatingCubeDemo> createState() => _RotatingCubeDemoState();
}

class _RotatingCubeDemoState extends State<RotatingCubeDemo> {
  GameEngineController? _controller;
  double _rotationSpeed = 45.0;
  String _rotationAxis = 'Y';
  Map<String, dynamic>? _cubeState;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          // Unity view
          GameWidget(
            engineType: GameEngineType.unity,
            config: GameEngineConfig(runImmediately: true),
            onEngineCreated: _onEngineCreated,
            onMessage: _onMessage,
          ),
          
          // Flutter UI overlay
          _buildControlPanel(),
        ],
      ),
    );
  }

  void _onEngineCreated(GameEngineController controller) {
    setState(() => _controller = controller);
  }

  void _onMessage(GameEngineMessage message) {
    if (message.target == 'GameFrameworkDemo') {
      switch (message.method) {
        case 'onStateUpdate':
          setState(() {
            _cubeState = jsonDecode(message.data);
          });
          break;
      }
    }
  }
  
  Widget _buildControlPanel() {
    return Positioned(
      bottom: 20,
      left: 20,
      right: 20,
      child: Card(
        color: Colors.black.withOpacity(0.7),
        child: Padding(
          padding: EdgeInsets.all(16),
          child: Column(
            children: [
              // Speed slider
              Text('Speed: ${_rotationSpeed.toStringAsFixed(0)}°/s'),
              Slider(
                value: _rotationSpeed,
                min: -180,
                max: 180,
                onChanged: (value) {
                  setState(() => _rotationSpeed = value);
                  _setSpeed(value);
                },
              ),
              
              // Axis buttons
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  _axisButton('X'),
                  _axisButton('Y'),
                  _axisButton('Z'),
                  _axisButton('All'),
                ],
              ),
              
              // Action buttons
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  ElevatedButton(
                    onPressed: _reset,
                    child: Text('Reset'),
                  ),
                  ElevatedButton(
                    onPressed: _getState,
                    child: Text('Get State'),
                  ),
                  ElevatedButton(
                    onPressed: _randomColor,
                    child: Text('Random Color'),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _axisButton(String axis) {
    return ElevatedButton(
      onPressed: () => _setAxis(axis),
      style: ElevatedButton.styleFrom(
        backgroundColor: _rotationAxis == axis 
          ? Colors.blue 
          : Colors.grey,
      ),
      child: Text(axis),
    );
  }

  void _setSpeed(double speed) {
    _controller?.sendJsonMessage('GameFrameworkDemo', 'setSpeed', {
      'speed': speed,
    });
  }

  void _setAxis(String axis) {
    setState(() => _rotationAxis = axis);
    _controller?.sendJsonMessage('GameFrameworkDemo', 'setAxis', {
      'axis': axis,
    });
  }

  void _reset() {
    _controller?.sendMessage('GameFrameworkDemo', 'reset', '');
  }

  void _getState() {
    _controller?.sendMessage('GameFrameworkDemo', 'getState', '');
  }

  void _randomColor() {
    _controller?.sendMessage('GameFrameworkDemo', 'randomColor', '');
  }
}

Unity Script

The GameFrameworkDemo.cs script is included in the templates. Key methods:

using UnityEngine;
using Xraph.GameFramework.Unity;

public class GameFrameworkDemo : FlutterMonoBehaviour
{
    protected override string TargetName => "GameFrameworkDemo";

    // Set rotation speed from Flutter
    [FlutterMethod("setSpeed")]
    public void SetSpeed(SpeedMessage message)
    {
        rotationSpeed = message.speed;
        SendStateUpdate();
    }

    // Set rotation axis from Flutter
    [FlutterMethod("setAxis")]
    public void SetAxis(AxisMessage message)
    {
        // Update rotation axis (X, Y, Z, or All)
        UpdateRotationAxis(message.axis);
        SendStateUpdate();
    }

    // Reset to defaults
    [FlutterMethod("reset")]
    public void Reset()
    {
        rotationSpeed = 45f;
        rotationAxis = Vector3.up;
        SendStateUpdate();
    }

    // Get current state
    [FlutterMethod("getState")]
    public void GetState()
    {
        SendStateUpdate();
    }

    // Apply random color
    [FlutterMethod("randomColor")]
    public void RandomColor()
    {
        cubeRenderer.material.color = Random.ColorHSV();
        SendStateUpdate();
    }

    // Send state to Flutter
    private void SendStateUpdate()
    {
        SendToFlutter("onStateUpdate", new StateMessage
        {
            speed = rotationSpeed,
            axis = GetAxisName(),
            rpm = rotationSpeed / 6f,
            color = ColorToHex(cubeRenderer.material.color)
        });
    }
}

Communication Flow

sequenceDiagram
    participant Flutter
    participant Unity
    
    Flutter->>Unity: setSpeed(45)
    Unity-->>Flutter: onStateUpdate(speed: 45, rpm: 7.5)
    
    Flutter->>Unity: setAxis("Y")
    Unity-->>Flutter: onStateUpdate(axis: "Y")
    
    Flutter->>Unity: randomColor()
    Unity-->>Flutter: onStateUpdate(color: "#FF5733")
    
    Flutter->>Unity: getState()
    Unity-->>Flutter: onStateUpdate(full state)

What You'll Learn

Bidirectional Communication

  • Sending messages from Flutter to Unity
  • Receiving responses from Unity to Flutter
  • Real-time parameter updates

Message Types

  • JSON Messages: Structured data with type safety
  • String Messages: Simple commands
  • State Updates: Continuous feedback

UI Patterns

  • Overlay UI on Unity view
  • Non-blocking interactive controls
  • Real-time state display

Unity Patterns

  • FlutterMonoBehaviour base class
  • [FlutterMethod] attribute for exposed methods
  • SendToFlutter() for responses

Tips & Tricks

Performance

The demo uses efficient messaging:

// Throttle slider updates
Timer? _throttle;

void _setSpeed(double speed) {
  _throttle?.cancel();
  _throttle = Timer(Duration(milliseconds: 16), () {
    _controller?.sendJsonMessage('GameFrameworkDemo', 'setSpeed', {
      'speed': speed,
    });
  });
}

Touch Controls

Unity side implements touch-based rotation:

void Update()
{
    // Touch input for spinning
    if (Input.touchCount > 0)
    {
        Touch touch = Input.GetTouch(0);
        if (touch.phase == TouchPhase.Moved)
        {
            // Apply angular velocity based on swipe
            ApplyTouchRotation(touch.deltaPosition);
        }
    }
}

Color Synchronization

Keep colors in sync between Flutter and Unity:

void _onMessage(GameEngineMessage message) {
  if (message.method == 'onStateUpdate') {
    final state = jsonDecode(message.data);
    setState(() {
      _currentColor = Color(
        int.parse(state['color'].substring(1), radix: 16) + 0xFF000000
      );
    });
  }
}

The demo is fully interactive - try adjusting the speed, changing axes, and applying random colors to see real-time communication in action!

Troubleshooting

Cube Not Visible

  • Ensure camera is positioned correctly
  • Check Unity lighting
  • Verify material is applied

No Communication

  • Check onEngineCreated is called
  • Verify script is attached in Unity
  • Check console for errors

UI Not Responsive

  • Ensure GameWidget is in Stack
  • Check z-index of overlay
  • Verify touch events aren't blocked

Next Steps