Arduino Skylanders

by mirthe2197 in Circuits > Arduino

130 Views, 1 Favorites, 0 Comments

Arduino Skylanders

Schermafbeelding 2024-06-30 002412.png

Skylanders is a game where you place real life figurines on a portal to ‘teleport’ them in game. I made a similar set up with Arduino!

Supplies

·      Arduino UNO

·      Wires

·      RFID-RC522 NFC Kit

·      LED and resistor (220 ohm) (optional)

·      A box (optional)

·      Unity Game Engine

·      3D model (printed and digital)

Set-Up Reader

Schermafbeelding 2024-06-29 233628.png

First set up the NFC reader. I did it according to the image.

Add LED

Schermafbeelding 2024-06-29 235113.png

I added a LED to make it more clear for myself when the chip was being read. So I did that according the scheme in the image.

Arduino Code

As for the code. I first wrote code on to make the reader work. I kept checking in the Serial reader if it gives me the notifications. Also to see the number of the chip you’re working with.


#include <SPI.h>
#include <MFRC522.h>


#define SS_PIN 10
#define RST_PIN 9


MFRC522 mfrc522(SS_PIN, RST_PIN);  // Create MFRC522 instance.

void setup() {
  Serial.begin(9600);  // Initialize serial communications with the PC
  SPI.begin();         // Init SPI bus
  mfrc522.PCD_Init();  // Init MFRC522
  Serial.println("Scan an NFC tag");
}


void loop() {
  // Look for new cards
  if ( ! mfrc522.PICC_IsNewCardPresent()) {
    return;
  }
  // Select one of the cards
  if ( ! mfrc522.PICC_ReadCardSerial()) {
    return;
  }
  // Show UID on serial monitor
  Serial.print("UID tag :");
  String content = "";
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
    Serial.print(mfrc522.uid.uidByte[i], HEX);
    content.concat(String(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "));
    content.concat(String(mfrc522.uid.uidByte[i], HEX));
  }
  Serial.println();
  delay(1000);
}


Then I added the LED portion:

#include <SPI.h>
#include <MFRC522.h>

#define SS_PIN 10
#define RST_PIN 9
const int ledPin = 7;

MFRC522 mfrc522(SS_PIN, RST_PIN);  // Create MFRC522 instance.

void setup() {
  Serial.begin(9600);  // Initialize serial communications with the PC
  SPI.begin();         // Init SPI bus
  mfrc522.PCD_Init();  // Init MFRC522
  Serial.println("Scan an NFC tag");
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // Look for new cards
  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }
  // Select one of the cards
  if (!mfrc522.PICC_ReadCardSerial()) {
    return;
  }
 
  Serial.print("UID tag :");
  String content = "";
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
    Serial.print(mfrc522.uid.uidByte[i], HEX);
    content.concat(String(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "));
    content.concat(String(mfrc522.uid.uidByte[i], HEX));
  }
  Serial.println(); // Ensure each message ends with a newline character
  Serial.println(content); // Send the UID again in a simpler format
 
  digitalWrite(ledPin, HIGH);
  delay(1000);
  digitalWrite(ledPin, LOW);
  delay(1000);
}


Unity

Make a project in Unity. Add an asset pack called Ardity.

Add the SerialController script Prefab to your scene and make sure the right Port is added in the Inspector.

Then create a scene with a Plane, your character model and an Empty Game Object called SpawnPoint.

Create a new Script NFC reader. Write the following code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class NFCReader : MonoBehaviour
{
    public SerialController serialController;
    public GameObject characterPrefab; // Reference to the character prefab
    public Transform spawnPoint; // Where the character will be spawned


    private GameObject spawnedCharacter; // Track the spawned character instance
    private bool hasSpawned; // Flag to track if the character has been spawned


    void Start()
    {
        serialController = GameObject.Find("SerialController").GetComponent<SerialController>();
    }


    void Update()
    {
        string message = serialController.ReadSerialMessage();
        if (message == null || hasSpawned)
            return;


        Debug.Log("Raw message received: " + message);
        ProcessData(message);
    }


    void ProcessData(string data)
    {
        // Extract the UID part from the message
        string trimmedData = ExtractUID(data);
        Debug.Log("Extracted UID: " + trimmedData);


        // Check if the UID matches the expected UID
        if (trimmedData == "UIDCHIPCODE" && !hasSpawned) // Replace with your NFC tag UID
        {
            SpawnCharacter();
            hasSpawned = true; // Set the flag to true after spawning
        }
        else
        {
            Debug.LogWarning("Unexpected UID: " + trimmedData);
        }
    }


    void SpawnCharacter()
    {
        // Spawn the character prefab at the spawn point
        spawnedCharacter = Instantiate(characterPrefab, spawnPoint.position, spawnPoint.rotation);
        Debug.Log("Character spawned: " + spawnedCharacter.name);
        spawnedCharacter.tag = "Player"; // Assigning "Player" tag to the spawned character


        // Attach the CameraFollow script to the main camera and set the target to the spawned character
        Camera.main.gameObject.AddComponent<CameraFollow>();
        CameraFollow cameraFollowScript = Camera.main.GetComponent<CameraFollow>();
        if (cameraFollowScript != null)
        {
            cameraFollowScript.SetTarget(spawnedCharacter.transform);
        }
        else
        {
            Debug.LogError("CameraFollow script not found on main camera.");
        }
    }


    string ExtractUID(string data)
    {
        // Find the index of the colon and extract the UID part
        int colonIndex = data.IndexOf(':');
        if (colonIndex >= 0 && colonIndex + 1 < data.Length)
        {
            // Extract substring after the colon and remove spaces
            string uidPart = data.Substring(colonIndex + 1).Replace(" ", "").Trim();
            return uidPart;
        }
        return data.Replace(" ", "").Trim();
    }
}

This script makes sure your character spawned when the card reader reads the script. Change the UIDCHIPCODE to the name of your chip that appeared in the Serial reader earlier. We use the UIDs, so we could spawn more characters with different chips.

Don't forget to assign your Spawnpoint and Character model in the inspector.

You'll see that the model will clone itself. If you don't want to see a döppelganger of your character in the scene, just move it far away so it will be out of sight.

Finishing Touches

To make the character move around, add a PlayerController Script to your original character with Rigidbody and a collider attached.

using UnityEngine;


public class PlayerController : MonoBehaviour
{
    public float speed = 6.0f;
    public float jumpHeight = 1.5f;
    public float gravity = -9.81f;
    public float turnSmoothTime = 0.1f;


    private CharacterController controller;
    private Vector3 velocity;
    private bool isGrounded;
    private float turnSmoothVelocity;


    void Start()
    {
        controller = GetComponent<CharacterController>();
    }


    void Update()
    {
        isGrounded = controller.isGrounded;


        if (isGrounded && velocity.y < 0)
        {
            velocity.y = -2f; // Ensure the character sticks to the ground
        }


        Move();
        Jump();
    }


    void Move()
    {
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");


        Vector3 direction = new Vector3(moveHorizontal, 0f, moveVertical).normalized;


        if (direction.magnitude >= 0.1f)
        {
            float targetAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
            float angle = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetAngle, ref turnSmoothVelocity, turnSmoothTime);
            transform.rotation = Quaternion.Euler(0f, angle, 0f);


            Vector3 moveDirection = Quaternion.Euler(0f, targetAngle, 0f) * Vector3.forward;
            controller.Move(moveDirection * speed * Time.deltaTime);
        }
    }


    void Jump()
    {
        if (Input.GetButtonDown("Jump") && isGrounded)
        {
            velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
        }


        velocity.y += gravity * Time.deltaTime;
        controller.Move(velocity * Time.deltaTime);
    }
}

Here's an example of how to make your character move around.


Other things you could add are a camera that follows the player around. You just add that script to the original character model as well. Here's mine:

using UnityEngine;


public class CameraFollow : MonoBehaviour
{
    public float smoothSpeed = 0.125f;
    public Vector3 offset;


    private Transform target; // Target transform for the camera to follow


    void LateUpdate()
    {
        if (target != null)
        {
            Vector3 desiredPosition = target.position + offset;
            Vector3 smoothedPosition = Vector3.Lerp(transform.position, desiredPosition, smoothSpeed);
            transform.position = smoothedPosition;


            transform.LookAt(target);
        }
    }


    public void SetTarget(Transform newTarget)
    {
        target = newTarget;
    }
}

Lastly, I added a bit of an environment (assets from Polytope Studio), and glued the chip to the bottom of my model... and done!