Game Framework
Examples

Messaging Patterns

Learn all the messaging patterns available in Game Framework: string messages, JSON/typed messages, request-response patterns, and event broadcasting.

Overview

The MessagingExample.cs template demonstrates:

  • String messaging for simple commands
  • JSON/typed messaging with automatic deserialization
  • Request-response pattern for async operations
  • Event broadcasting for game events
  • Error handling and validation

This example covers all the messaging patterns you'll need for production applications.

Setup

Sync Templates

game sync scripts --templates

Unity Setup

  1. Create an empty GameObject named Messaging
  2. Attach MessagingExample.cs script
  3. Configure in Inspector if needed

Message Types

1. String Messaging

Simple text-based communication.

Flutter → Unity

// Send string message
await controller.sendMessage(
  'Messaging',
  'sendString',
  'Hello Unity!'
);

Unity Script

[FlutterMethod("sendString")]
public void ReceiveString(string message)
{
    Debug.Log($"String received: {message}");
    
    // Echo back
    SendToFlutter("onStringReceived", $"Echo: {message}");
}

Unity → Flutter

controller.messageStream.listen((msg) {
  if (msg.method == 'onStringReceived') {
    print('Received: ${msg.data}');
  }
});

2. JSON/Typed Messaging

Structured data with type safety.

Define Message Classes

[System.Serializable]
public class TypedMessage
{
    public string action;
    public int value;
    public bool enabled;
}

[System.Serializable]
public class TypedResponse
{
    public string originalAction;
    public int processedValue;
    public string timestamp;
}

Flutter → Unity

// Send typed message
await controller.sendJsonMessage('Messaging', 'sendTyped', {
  'action': 'greet',
  'value': 42,
  'enabled': true,
});

Unity Script

[FlutterMethod("sendTyped")]
public void ReceiveTyped(TypedMessage message)
{
    Debug.Log($"Typed: action={message.action}, value={message.value}");
    
    // Process and respond
    var response = new TypedResponse
    {
        originalAction = message.action,
        processedValue = message.value * 2,
        timestamp = DateTime.UtcNow.ToString("o")
    };
    
    SendToFlutter("onTypedReceived", response);
}

Unity → Flutter

controller.messageStream.listen((msg) {
  if (msg.method == 'onTypedReceived') {
    final data = jsonDecode(msg.data);
    print('Action: ${data['originalAction']}');
    print('Value: ${data['processedValue']}');
    print('Time: ${data['timestamp']}');
  }
});

3. Request-Response Pattern

Async operations with callbacks.

Flutter → Unity

// Send request
final requestId = DateTime.now().millisecondsSinceEpoch.toString();

await controller.sendJsonMessage('Messaging', 'request', {
  'requestId': requestId,
  'operation': 'calculate',
  'params': {
    'a': 10,
    'b': 20,
  },
});

// Listen for response
controller.messageStream.listen((msg) {
  if (msg.method == 'onResponse') {
    final data = jsonDecode(msg.data);
    if (data['requestId'] == requestId) {
      print('Result: ${data['result']}');
    }
  }
});

Unity Script

[FlutterMethod("request")]
public void HandleRequest(RequestMessage request)
{
    Debug.Log($"Request {request.requestId}: {request.operation}");
    
    // Process request
    var result = ProcessOperation(request.operation, request.params);
    
    // Send response
    SendToFlutter("onResponse", new ResponseMessage
    {
        requestId = request.requestId,
        success = true,
        result = result,
        error = null
    });
}

4. Event Broadcasting

Broadcast game events to Flutter.

Unity Script

// Broadcast event to Flutter
public void BroadcastEvent(string eventType, object eventData)
{
    SendToFlutter("onGameEvent", new GameEvent
    {
        type = eventType,
        data = eventData,
        timestamp = Time.time
    });
}

// Example usage
void OnPlayerScored(int points)
{
    BroadcastEvent("score", new { points = points, total = totalScore });
}

void OnGameOver()
{
    BroadcastEvent("gameOver", new { finalScore = totalScore });
}

Flutter

controller.messageStream.listen((msg) {
  if (msg.method == 'onGameEvent') {
    final event = jsonDecode(msg.data);
    
    switch (event['type']) {
      case 'score':
        _handleScore(event['data']);
        break;
      case 'gameOver':
        _handleGameOver(event['data']);
        break;
    }
  }
});

Complete Flutter Example

import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:gameframework/gameframework.dart';

class MessagingExample extends StatefulWidget {
  @override
  State<MessagingExample> createState() => _MessagingExampleState();
}

class _MessagingExampleState extends State<MessagingExample> {
  GameEngineController? _controller;
  final _messageController = TextEditingController();
  List<String> _messages = [];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Messaging Patterns')),
      body: Column(
        children: [
          // Unity view
          Expanded(
            flex: 2,
            child: GameWidget(
              engineType: GameEngineType.unity,
              config: GameEngineConfig(runImmediately: true),
              onEngineCreated: (controller) {
                setState(() => _controller = controller);
              },
              onMessage: _onMessage,
            ),
          ),
          
          // Message log
          Expanded(
            child: ListView.builder(
              itemCount: _messages.length,
              itemBuilder: (context, index) {
                return ListTile(
                  title: Text(_messages[index]),
                  dense: true,
                );
              },
            ),
          ),
          
          // Controls
          Padding(
            padding: EdgeInsets.all(8),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _messageController,
                    decoration: InputDecoration(
                      hintText: 'Enter message',
                      border: OutlineInputBorder(),
                    ),
                  ),
                ),
                SizedBox(width: 8),
                ElevatedButton(
                  onPressed: _sendString,
                  child: Text('Send String'),
                ),
                SizedBox(width: 8),
                ElevatedButton(
                  onPressed: _sendTyped,
                  child: Text('Send Typed'),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  void _onMessage(GameEngineMessage message) {
    if (message.target == 'Messaging') {
      setState(() {
        _messages.add('← ${message.method}: ${message.data}');
      });
    }
  }

  void _sendString() {
    final text = _messageController.text;
    if (text.isNotEmpty) {
      _controller?.sendMessage('Messaging', 'sendString', text);
      setState(() {
        _messages.add('→ sendString: $text');
      });
      _messageController.clear();
    }
  }

  void _sendTyped() {
    _controller?.sendJsonMessage('Messaging', 'sendTyped', {
      'action': 'test',
      'value': Random().nextInt(100),
      'enabled': true,
    });
    setState(() {
      _messages.add('→ sendTyped: {action: test, ...}');
    });
  }
}

Message Flow Diagrams

String Messaging

sequenceDiagram
    participant Flutter
    participant Unity
    
    Flutter->>Unity: sendMessage("Hello")
    Unity->>Unity: Process message
    Unity-->>Flutter: onStringReceived("Echo: Hello")

Request-Response

sequenceDiagram
    participant Flutter
    participant Unity
    
    Flutter->>Unity: request(id: 123, operation: "calc")
    Unity->>Unity: Process operation
    Unity-->>Flutter: onResponse(id: 123, result: 42)

Event Broadcasting

sequenceDiagram
    participant Unity
    participant Flutter
    
    Note over Unity: Game event occurs
    Unity->>Flutter: onGameEvent(type: "score")
    Unity->>Flutter: onGameEvent(type: "powerup")
    Unity->>Flutter: onGameEvent(type: "gameOver")

Best Practices

1. Use Appropriate Message Types

// Simple commands → String
controller.sendMessage('Player', 'jump', '');

// Structured data → JSON
controller.sendJsonMessage('Player', 'moveTo', {
  'x': 10.5,
  'y': 0,
  'z': 20.3,
});

// Binary data → Binary
controller.sendBinaryMessage('Texture', 'upload', imageBytes);

2. Type Safety

Always define classes for typed messages:

// Good - Type safe
[System.Serializable]
public class MoveCommand
{
    public float x;
    public float y;
    public float z;
}

[FlutterMethod("moveTo")]
public void MoveTo(MoveCommand cmd) { }

// Avoid - Untyped
[FlutterMethod("moveTo")]
public void MoveTo(string json) { }

3. Error Handling

[FlutterMethod("riskyOperation")]
public void RiskyOperation(OperationRequest request)
{
    try
    {
        var result = PerformOperation(request);
        SendToFlutter("onSuccess", result);
    }
    catch (Exception e)
    {
        SendToFlutter("onError", new ErrorMessage
        {
            error = e.Message,
            requestId = request.id
        });
    }
}

4. Async Operations

Use request IDs for tracking:

class RequestManager {
  final _pending = <String, Completer>{};

  Future<dynamic> request(
    GameEngineController controller,
    String target,
    String method,
    Map<String, dynamic> params,
  ) {
    final id = Uuid().v4();
    final completer = Completer();
    _pending[id] = completer;

    controller.sendJsonMessage(target, method, {
      'requestId': id,
      ...params,
    });

    return completer.future;
  }

  void handleResponse(String id, dynamic data) {
    _pending[id]?.complete(data);
    _pending.remove(id);
  }
}

Performance Tips

Message Batching

For high-frequency updates:

// Enable batching in Unity
protected override void Awake()
{
    base.Awake();
    EnableBatching = true;
    BatchInterval = 0.016f; // 60 FPS
}

Throttling

Throttle rapid updates in Flutter:

Timer? _throttle;

void _sendUpdate(Map<String, dynamic> data) {
  _throttle?.cancel();
  _throttle = Timer(Duration(milliseconds: 16), () {
    controller?.sendJsonMessage('Target', 'update', data);
  });
}

Master these messaging patterns and you'll be able to build any Flutter-Unity integration!

What You'll Learn

  • ✅ String, JSON, and typed messaging
  • ✅ Request-response patterns
  • ✅ Event broadcasting
  • ✅ Error handling
  • ✅ Type safety
  • ✅ Performance optimization

Next Steps