Choose how to split functionality between my components in an ECS - swift

I am currently building a game and I want to use Gameplaykit to organise the elements.
I discovered the concept of ECS (Entity Component System) that enables to put functionality in small blocks (called components) that can than be added to an entity to give it functionality.
I would like to use it combined with SpriteKit, my first idea was of course to create a SpriteComponent, but since I am also using the physics engine of SpriteKit, I am wondering how I should make the PhysicsBodyComponent, because for this component to work it would need direct access to the physics body of my SpriteKit node, so it would be dependent of the SpriteComponent, but isn't it wrong for components to be dependent on each other ?
How should I do that ?
Thank you.

It depends how you want to structure your components. Like you said its best to keep them as flexible, reusable and independent of each other as much as possible.
So I would just create a simple render component like you want to. This is what I use in my games.
import SpriteKit
import GameplayKit
/// GK sprite component
class SpriteComponent: GKComponent {
// MARK: - Properties
/// The rendered node
let node: SKSpriteNode
// MARK: GKComponent Life Cycle
/// Init with image
init(imageNamed: String) {
node = SKSpriteNode(imageNamed: imageNamed)
}
/// Init with texture
init(texture: SKTexture) {
node = SKSpriteNode(texture: texture)
}
}
In your entity classes you add the component as usual
class Player: GKEntity {
override init() { // pass in an image name if you need to
super.init()
let spriteComponent = SpriteComponent(imageNamed: "Player")
addComponent(spriteComponent)
}
}
Than you add the entity to the scene and position it.
Once a component is added to an entity you can use the component(ofType: ...) method to access its properties, in this case the node property, and do something with them.
class GameScene: SKScene {
// code to add entity to scene
...
// position entity
if let playerNode = player.component(ofType: SpriteComponent.self)?.node {
playerNode.position = CGPoint(...)
}
Go back to your entity class and add the physics body after you added the sprite component.
...
addComponent(spriteComponent)
// Set physics body
if let sprite = component(ofType: SpriteComponent.self)?.node { // component for class is an optional and nil until component is added to entity.
sprite.physicsBody = SKPhysicsBody(...
sprite.physicsBody?.categoryBitMask = ...
...
}
This way all your entities can use 1 render component but have different physics bodies on them and use different positions.
You could create a physics body component and pass into the init method the bit masks etc and the node you want it to be added to. However I think thats makes it quite messy so I prefer this way.
If you do need to make components dependent of each other remember that each GKComponent has an entity property you can use. I would try to avoid this as much as possible to keep your components more flexible.
class SomeComponent: GKComponent {
func test() {
entity?.component(ofType: SomeOtherComponent.self)?.someMethod() // only works if this component is added to entity (entity?) and the other component is also added to entity (...self)?.
}
class SomeOtherComponent: GKComponent {
func someMethod() {
}
}
If you need more info you should read these articles, they are very good.
https://www.raywenderlich.com/119959/gameplaykit-tutorial-entity-component-system-agents-goals-behaviors
http://code.tutsplus.com/tutorials/an-introduction-to-gameplaykit-part-1--cms-24483
Hope this helps

Related

Unity MVC get View atached to the UI label

A simple task. I have a ScoreController which will set the score to UI. I have a UI label that has ScoreView in it
The question is how do I get this ScoreView in my controller
I have found out the way to get View from prefab that's being instantiated:
public class CellViewFactory : ICellViewFactory
{
public ICellView GetView(Vector3 position)
{
var prefab = Resources.Load<GameObject>("Cell");
var instance = UnityEngine.Object.Instantiate(prefab);
var cellView = instance.GetComponent<ICellView>();
cellView.SetPosition(position);
return cellView;
}
}
But since UI isn't instantiated by the code, what's the way to get the ScoreView from it?
Now, I have found an option to call a static method of a Factory and pass self to the method. Smth like this:
public class ScoreView : MonoBehaviour
{
void Start()
{
ScoreViewFactory.NotifyScoreViewCreated(this);
}
}
But it doesn't seem to be flexible using static methods in decoupled code with Factories and interfaces. Hope there's another way
I would also be glad if you post some complete tutorioal on MVC in Unity. Seems that there's no complete working tutorials.
It would also be great if you would share an example of MVC pattern in Unity using DependencyInjection.

Destroy specific child object of prefab object in c# / Unity

I search a lot but couldn't find any answer. My problem: I instantiate several Gameobjects of a Prefab, each consists of two child objects: an obstacle and a scorezone. I want to destroy the scorezone (only of that specific instance), when the player touches the obstacle. So in Runtime my hierarchy looks like this:
-ObjectPrefab(clone)
---Scorezone
---Obstacle
-ObjectPrefab(Clone)
---Scorezone
---Obstacle
...
So I need to find ONE child gameobject (not the Transform) to then destroy it. I tried several codes but none worked. Here is the code with the three alternatives I tried, but none of them worked.:
private void OnCollisionEnter2D(Collision2D collision) {
if (collision.gameObject.tag == "obstacle") {
Alternative 1:
Destroy(GameObject.FindWithTag("scorezone"));
Alternative 1 comes really close, but it destroys all scorezones of all instantiated objects.
Alternative 2:
Destroy(collision.gameObject.transform.parent.gameObject.FindWithTag("scorezone"));
Alternative 2 would sound logic to me but I get the error message "Transform does not contain a definition for "FindWithTag".
Alternative 3:
foreach (Transform child in collision.gameObject.transform)
{
if (child.tag == "scorezone")
{
Destroy(child.gameObject);
}
}
}
Alternative 3 does not give an error but actually does nothing at all when the player hits the obstacle.
I really appreciate some help, thanks in advance!
Store the reference!
If it is really a "prefab" so meaning you have it as an asset and instantiate it on runtime the best way (in my eyes and performance wise) would actually be to use a serialized field:
// Put this component on your obstacle
public class ObjectController : MonoBehaviour
{
// Drag the according object here via the Inspector in the prefab
// => when the prefab is Instantiated this field is already referenced
[SerializeField] private GameObject _scoreZone;
// Read-only access to the reference
public GameObject ScoreZone => _scoreZone;
}
This field reference is stored together with your prefab asset. So every instance of the prefab already has its own child references set up.
and then do
private void OnCollisionEnter2D(Collision2D collision)
{
// Rather use CompareTag which is slightly more efficient
// and adds some security (== silently fails if typo, CompareTag throws an exception instead)
if (collision.gameObject.CompareTag("obstacle"))
{
var controller = collision.gameObject.GetComponent<ObjectController>();
if(controller)
{
Destroy(controller.ScoreZone);
}
}
}
By Index
Alternatively but error prone you could simply use the index. You already know that there are only 2 childs, the first one being the Scorezone with index 0 so you can also do
if (collision.gameObject.CompareTag("obstacle"))
{
var scoreZone = collision.transform.parent.GetChild(0);
Destroy(scoreZone);
}
By Name
Using the name you could also use Find - I would not recommend this! It is error prone and slow
if (collision.gameObject.CompareTag("obstacle"))
{
var scoreZone = collision.transform.parent.Find("ScoreZone");
Destroy(scoreZone);
}

Scriptable Object as a custom version of sprite

I have made a scriptable object which has a one property a public sprite. I want to have one version of sprite added everywhere I want.
With the possibility that if I ever want to change it I will just pin another sprite to scriptable object and in every place where it is pinned the sprite will change.
Is there any way to walk thought this problem or any other idea to make one object where I will store one Sprite and pin it to multiple objects.
Finally is this concept possible in Unity?
I have already thought about:
Deriving prom Sprite Renderer but the class is sealed, so i cannot make my own version of it this way
Creating my own custom version of Sprite Render cause if an any update come I would have to make yet again another version.
I think you can use prefabs to achieve the same thing as you need:
You can create prefab by dragging and object to your assets.(Your prefab will be just an object with transform and e.g. Sprite Renderer)
After creating prefab you can copy it as many times as you want.
Than when you decide to change the sprite just simply go to your prefab and edit sprite there. Now every instance of your prefab will have sprite changed.
It is possible with C# events. First, make your ScriptableObject call event whenever sprite is set. Allow Sprite to be set only using property (or method), so that you could track the change, like this:
public sealed class SpriteObject : ScriptableObject
{
public event Action SpriteChanged;
[SerializeField]
private Sprite _sprite;
public Sprite Sprite
{
get { return _sprite; }
set
{
if(_sprite == value)
return;
_sprite = value;
SpriteChanged?.Invoke();
}
}
}
Then, you need script, that will react to changed and assign the changed sprite to SpriteRenderer. So, something like this:
NOTE: name is purely for example. Do NOT name your classes like this!
[RequireComponent(typeof(SpriteRenderer))]
public class ScriptThatUsedSpriteObject : MonoBehaviour
{
public SpriteObject spriteObject;
private SpriteRenderer spriteRenderer;
/// Called once the script is created.
private void Awake()
{
spriteRenderer = GetComponent<SpriteRenderer>();
if(spriteObject != null)
{
spriteRenderer.sprite = spriteObject.Sprite;
spriteObject.SpriteChanged += HandleSpriteChanged;
}
}
/// Called whenever sprite is changed inside SpriteObject.
private void HandleSpriteChanged()
{
spriteRenderer.sprite = spriteObject.Sprite;
}
}
Now, if some script changes sprite in SpriteObject, every ScriptThatUsedSpriteObject will automatically update their sprites.
public class PublicSprite : MonoBehaviour {
public Sprite publicSprite;
#region Singelton
public static PublicSprite instance;
private void Awake()
{
if (instance != null)
{
Debug.LogWarning("More than one instance found");
return;
}
instance = this;
}
}
get the sprite like this:
PublicSprite.instance.publicSprite;
as Mentioned here, you probably want to use a Singelton. It is best to avoid the use of them only if its necessary as its a very bad programming practice. (you can learn more on why to avoid them here: What is so bad about singletons?
However, I can't think of another way of doing what you seek for.

SpriteKit Lots of Nodes from Same Image: Load Separately or Duplicate?

I'm programming a game in SpriteKit and am coding a section where the "level" is loaded in from a text file, placing a wall node in every spot marked by an "x" in the text file. However, if I know that there are going to be a lot of nodes, and they're all being loaded from the same "wall.png" file, is it more efficient to load the image once and then duplicate the object each time needed, or just load the image each time?
for line in lines {
for letter in line {
if letter == "x" {
let wall = SKSpriteNode(imageNamed: "wall")
self.addChild(wall)
} else { ... }
}
}
VS
let wall = SKSpriteNode(imageNamed: "wall")
for line in lines {
for letter in line {
if letter == "x" {
self.addChild(wall.copy())
} else { ... }
}
}
self in this scope is a class holding the level that extends SKNode, so I'm adding walls to self and then adding that SKNode to the scene.
To answer your question without resorting to 3rd party support
Go with the 2nd option (the copy option)
This will use the same texture across multiple sprites, where as the 1st choice creates a new texture every iteration.
The approach to developing your project remember me a TileMap. Pay attention because you can earn a lot of time instead to load each elements, you can prepare your levels and you have more fun.
There are thousand of tutorials that can help you to build a TileMap with Sprite-kit and Swift. Many of them use this GitHub library called also JSTileMap here
In these tutorials you can learn to how to:
Create a map with Tiled
Add the map to the game
Scroll the map to follow the player
Use object layers.
It's very simple , for example you can load a tmx map:
let tiledMap = JSTileMap("mapFileName.tmx") //map name
if t = tileMap {
self.addChild(tiledMap)
}

Swift - How to assign var to unknown class

Background: I'm creating a game where two objects(Tanks) battle one another.
On the main menu screen, you choose the two Tanks that will be fighting one another (out of a list of 10 different tanks).
The algorithms for moving and firing the individual/specific Tanks comes from a separate .swift (helper) class for each player that can be chosen from the menu.
(there is no user interaction - all of the code for the battle is inside these classes)
Inside the individual player classes there are only two functions defined: player1TankMove() and player2TankMove().
class Player1: gameSceneDelegate {
func player1TankMove() {
// some code that defines the movement and shooting of the Tank
}
func player2TankMove() {
// some code that defines the movement and shooting of the Tank
}
}
There would be ten of these classes (Player1, Player2, Player3...) where the player1TankMove() methods for each class would be completely different from one another.
As you can see, I'm using a delegate from the GameScene to connect the TankMove() functions so that they can be used inside that class:
protocol gameSceneDelegate {
func player1TankMove()
func player2TankMove()
}
class GameScene: SKScene, SKPhysicsContactDelegate { ... //entire GameScene code
My question is, how do I set the proper class when I transition from the MainMenuViewController to the GameScene class?
Would I use lazy instantiation? If so, how would I declare it 'lazily' if I don't know which class/swift file (Player1.swift, Player2.swift, etc.) I'll be accessing?
Is there a better way to do this?
Currently I can access the proper player1TankMove() function and the tank works properly:
var tank1 = Player1()
tank1.player1TankMove() // used inside the update() function
However, because tank1 needs to be an instance of a specific class that is unknown, I'm unsure of how to decide the type to declare. Would I need to use a typealias? Generics? It seems like this would be a fairly common problem that I know how to handle with other languages, but I'm not sure how to code it in Swift.