GithubHelp home page GithubHelp logo

johnsundell / imagineengine Goto Github PK

View Code? Open in Web Editor NEW
1.8K 77.0 113.0 1.37 MB

A project to create a blazingly fast Swift game engine that is a joy to use πŸš€

License: Other

Swift 98.44% Ruby 1.56%
game-development swift game-engine 2d-game-engine coreanimation

imagineengine's Introduction

Imagine Engine

CocoaPods Carthage Twitter: @johnsundell

Welcome to Imagine Engine, an ongoing project that aims to create a fast, high performance Swift 2D game engine for Apple's platforms that is also a joy to use. You are hereby invited to participate in this new community to build a tool with an ambitious but clear goal - to enable you to easily build any game that you can imagine.

Fast Core Animation-based rendering

Imagine Engine uses Core Animation as its rendering backend - just like Apple's UI frameworks like UIKit and AppKit do. By leveraging the power of Core Animation's hardware accelerated 2D rendering capabilities, Imagine Engine is able to push lots of pixels onto the screen at the same time. That means more objects, more effects and less restrictions when designing your games.

An easy to use API

Besides its goal of being blazingly fast at rendering & updating your games, Imagine Engine aims to provide an easy to use API that anyone can learn - regardless of game development experience.

Start with just a few lines of code...

let scene = Scene(size: UIScreen.main.bounds.size)

let label = Label(text: "Hello world")
label.position = scene.center
scene.add(label)

let window = GameWindow(scene: scene)
window.makeKeyAndVisible()

...and smoothly scale up as your game grows in complexity on either iOS, macOS or tvOS.

πŸŒƒ Scenes present your game content

A scene can be a level, a menu or a "Game over" screen. You can easily switch the active scene of a game. Here's how you can create a scene with a blue background color:

let scene = Scene(size: Size(width: 500, height: 300))
scene.backgroundColor = .blue
game.scene = scene

🎭 Actors bring your game to life

Actors are what will make up most of the active objects in any game. They are movable, animatable, can handle collisions and much more. Here's an example of how you can create a player that renders a "Running" animation, and constantly moves to the right:

let player = Actor()
player.animation = Animation(name: "Running", frameCount: 5, frameDuration: 0.15)
player.velocity.dx = 50
scene.add(player)

πŸ“¦ Easily create platforms and tiled textures with Blocks

Using blocks you can easily tile textures together to form objects that can scale nicely to any size, without having to scale any texture. This is done by stitching together up to 9 different textures to form a block of textures rendered side by side. Here's how you can easily create a block from a folder named "Platform" that contains the textures that should be stitched together:

let block = Block(size: Size(width: 300, height: 300), textureCollectionName: "Platform")
scene.add(block)

πŸ…°οΈ Render text using Labels

Labels let you add text content to your game. They automatically resize to fit your text content (unless you don't want them to) and can be used to implement things like UI, score counters, etc. Here's an example of adding a label to a scene:

let label = Label(text: "Welcome to my game!")
label.position = scene.center
scene.add(label)

⚑️ Use Events to drive your game logic

Events enable you to quickly script your games to drive your own logic. Imagine Engine's various objects contain built in events that can be used to observe whenever an object was moved, collided with something, etc. You can also define your own events that can be used to communicate between various parts of your code. Here's how you can observe whenever two actors collided with each other:

let player = Actor()
let enemy = Actor()

player.events.collided(with: enemy).observe {
    // Game over
}

πŸƒ Create animations and effects using Actions

Actions let you make objects do something over a period of time, for example moving, resizing, fading in and out etc. Imagine Engine contains a suite of built-in actions and also makes it easy for you to define your own. Here's how an actor can be moved over 3 seconds:

let actor = Actor()
scene.add(actor)
actor.move(byX: 200, y: 100, duration: 3)

πŸ”Œ Easily extend Imagine Engine with Plugins

Instead of relying on subclassing and overriding methods, Imagine Engine is designed to be easily extended through plugins. This enables you to share code between different games, and create new open source projects that add new functionality to the engine. You can attach plugins to most of Imagine Engine's objects, here's an example of creating a plugin that creates a new actor every time the scene is clicked or tapped:

class MyPlugin: Plugin {
    func activate(for scene: Scene, in game: Game) {
        scene.events.clicked.observe { scene in
            let actor = Actor()
            actor.position = scene.center
            scene.add(actor)
        }
    }
}

πŸ• Precise timing using Timelines

Managing time and delayed events can sometimes be tricky in game development. Imagine Engine aims to make this a lot easier through its timeline API, that enables you to schedule single or repeated events in the future without having to worry about screen updates or if the game is paused. Here's how you can add an event to spawn a new enemy every 5 seconds:

scene.timeline.repeat(withInterval: 5) {
    let enemy = Actor()
    enemy.animation = Animation(name: "Enemy", frameCount: 5, frameDuration: 0.15)
    scene.add(enemy)
}

Platform support

  • πŸ“± iOS 9 or later
  • πŸ–₯ macOS 10.12 or later
  • πŸ“Ί tvOS 10 or later

Imagine Engine supports all of Apple's platforms except watchOS. The API is also completely cross platform, so that you don't have to scatter #ifs all over your game code.

Xcode templates

Imagine Engine ships with Xcode project templates that makes it super easy to get started with a new project. You can find more information & installation instructions here.

Let's get started!

To get started, check out the tutorials section, which contains tutorials that will walk you through building your first Imagine Engine-powered games with very few lines of code. No previous game developer experience required!

If you need help getting started or have a question about Imagine Engine, feel free to open an issue! We're a friendly community who would love to get more people involved.

Imagine Engine is in active development, with new features being constantly added. Need something new, or want to help out making the engine even more capable? Browse and create new issues or open a PR.

Lets build some awesome games together! πŸš€

imagineengine's People

Contributors

alex88wh avatar aranasaurus avatar canintospace avatar dymv avatar insidegui avatar johnsundell avatar juanpe avatar jussi80 avatar kkapitan avatar krausefx avatar louisdebaere avatar mattiashagstrand avatar msaps avatar nsmyself avatar pablocarmu avatar pedrovereza avatar tadeaskriz avatar vijaytholpadi avatar warren-lamb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

imagineengine's Issues

Change Size of Actor

Hi, i read the first tutorial and then try to make something different.
I find some different assets and it was bigger than first tutorial's resources.
So i tried this
let enemy = Actor(size: Size(width: 0.5, height: 0.5))
enemy.animation = Animation(name: "Enemies", frameCount: 8, frameDuration: 0.02)
so it did not change the size of Actor. Also i tried to set size after initialization of actor and animation like
enemy.size = Size(width: 0.5, height: 0.5)
And again did not change the size.

When i tried to set scale

enemy.scale = 0.3
enemy.hitboxSize!.height = enemy.size.height * 0.3
enemy.hitboxSize!.width = enemy.size.width * 0.3

it works to change size of image but hit boxes were same size of orginal image size.

README section with why this engine stands out

I think it would be nice to have a section on the README file showing why someone would want to use ImagineEngine over cocos2d or SpriteKit.
Like, what characteristics make this one stand out, why you designed it. 😊

Enable Label to support multiple lines of text

Currently, the Label class only supports rendering a single line of text, and (per default) automatically resizes itself to fit that line of text. The auto-resizing can already be disabled by setting shouldAutoResize to true, but still it'll only render on a single line.

The good news is that CATextLayer (which Label uses for rendering) already supports multiple lines of text, we just need to add support for setting the isWrapped property on it to true. I suggest adding a shouldWrap property (to match shouldAutoResize in terms of naming) to Label which in turn toggles isWrapped on the underlying layer.

Make Label scalable

You should be able to scale a Label using the same API that you can use for actors. Functionality like this is implemented in a protocol oriented fashion, so all that needs to be done is to make Label conform to Scalable. See Actor's implementation of this protocol for reference.

Frame by Frame Sprite Control

How would we achieve frame by frame control over a sprite?

Let's imagine a scenario from another pseudo engine as an example:

Every frame an update() function is called.

Inside update I want to set the current frame for the sprite.

My reasoning is that I want to process all my input controls and interactions with the game world, as well as the past few moments to decide which animation frame to show in the present.

This would allow some dynamic transitions between animation loops where another input button is pressed before the previous animation completes.

[Help] How to use ImagineEngine in native development

Hi John,
I'm opening this issue after our small twitter ping-pong chat.
I'm here to ask for your help on how to incorporate the ImagineEngine in the context of a native app. I started messing around the playground you created, and then I tried to port it to a full fledged app starting a blank application.
I went through the code and came up that the GameViewController is a (closed) subclass of UIViewController, and that we have to feed it the Scene class where we describe the state of the components, actors and stuff. However (I didn't spend a lot of time into it yet), I couldn't get it to load the playground code in the app as I wasn't able to figure out how to transition between viewcontrollers (is that even possible?). A full fledged app would have more than one gameviewcontroller? Is the game flow represented only by scenes in one gameviewcontroller?

Thanks in advance

Repeat action causes small stutter

When repeating an action (e.g. rotation) each time the action repeats their is a small stutter.
This is caused by the action reset because an action rarely ends at a completion ratio of exactly one.

Changing

internal func reset() {
    startTime = nil
    lastUpdateTime = nil
}

to

internal func reset() {
    startTime = lastUpdateTime
}

in Action seems to fix the problem in the case when an action is repeated but could potentially cause problems in other cases. Maybe Action should have a restartmethod? In that case the responsibility for calling resetor restart probably needs to be moved out of ActionWrapper.

Add APIs to check if an actor is in contact with another actor or block

It would be really nice to be able to check if an actor is currently in contact with another actor or block, like this:

let isInContactWIthOtherActor = actor.isInContact(with: anotherActor)
let isInContactWithBlock = actor.isInContact(with: block)

Thankfully this is super easy to implement, since Actor already has internal properties that keep track of its current contacts:

internal lazy var actorsInContact = Set<Actor>()
internal lazy var blocksInContact = Set<Block>()

Things to do

  • Implement the two methods that enables the API user to check if an actor is currently in contact with another actor or block.
  • Use the contact sets to return true or false.
  • Unit test by moving an actor into contact with another actor and block and verify that the method returns the correct result.

Add 'enteredScene' event for actors

Actor already has a leftScene event that gets triggered whenever it leaves the bounds of its scene. We should add a enteredScene as well that gets triggered whenever an actor moves from being completely outside of its scene to being within its bounds.

Good to know:

  • Actor events are defined on ActorEventCollection.
  • The leftScene event is triggered by Scene in actorRectDidChange(), which each actor calls whenever its rect was changed.
  • Actor has a isWithinScene property that is used to track whether an actor is currently within its scene's bounds or not.

Automatically load textures with higher resolution than the screen

Currently, we only automatically fall back to textures with a lower resolution than the screen that the game is running on (for example, if the game is running on an iPhone X and there's no @3x asset, then the @2x or @1x version will be automatically loaded).

We should do the same thing for falling back to a higher resolution as well, that is if the screen is an @1x screen but there's only an @2x asset, that asset should be loaded.

Texture loading takes place in TextureManager and BundleTextureImageLoader.

XCTest Performance Testing

We should investigate if we can use XCTest's performance testing features to implement automatic performance tests on CI. This to prevent regressions in high performance-sensitive code when adding new features.

APIs that would be good to performance test:

  • Timeline, since it's the backbone of all updates, including actions & animations.
  • Action, since it is the base class for all actions.
  • TextureManager, since it loads all textures, which is a very common operation.

Questions to discuss

  • Can we run these type of performance tests on CI without causing flakiness?

Tutorial build errors on Xcode 8.3.3

Downloaded and tried to build the Tutorial and I get errors:

β€œSwift Language Version” (SWIFT_VERSION) is required to be configured correctly for targets which use Swift. Use the [Edit > Convert > To Current Swift Syntax…] menu to choose a Swift version or use the Build Settings editor to configure the build setting directly.

β€œSwift Language Version” (SWIFT_VERSION) is required to be configured correctly for targets which use Swift. Use the [Edit > Convert > To Current Swift Syntax…] menu to choose a Swift version or use the Build Settings editor to configure the build setting directly.

warning: no umbrella header found for target 'ImagineEngine-iOS', module map will not be generated

I tried what it suggested but it didn't fix it. It's been a long time since I've used Xcode so I'm not exactly sure what to do about it.

I'm using Xcode 8.3.3 on Mac OS X 10.12.6.

Document how Imagine Engine can be used in Swift Playgrounds on iPad

Would be great if ImagineEngine could be used in iPad Playgrounds.

As a first step I tried to put the folders β€žAPIβ€œ, β€žInternalβ€œ and β€žUIKItβ€œ in xCode into an iOS Playground's β€žSourcesβ€œ Folder. Unfortunately xCode is stuck on β€žCompiling ImageEngineTestβ€œ for over an hour now.

Can this be achieved somehow? Having ImagineEngine on iOS Playgrounds App would be huge!

Thanks and keep up the good work!

module file was created for incompatible target x86_64-apple-macosx10.12

Playground execution failed:

error: Playground.playground:3:8: error: module file was created for incompatible target x86_64-apple-macosx10.12: /Users/user/Library/Developer/Xcode/DerivedData/AsteroidBlaster-cxopjqrvbpqmgucxltcgjmcfwabc/Build/Products/Debug/ImagineEngine.framework/Modules/ImagineEngine.swiftmodule/x86_64.swiftmodule
import ImagineEngine
^

Extra collision event when actor moves between grid tiles

After two actors have collided there is normally only one collision event even if they move around, as long as they remain in contact. However, if one of the actors move out of a grid tile that both actors are in and then moves back into it, an extra event is generated. Here is a test that shows this happening:

func testObservingCollisionsWhileInContactAndMovingBetweenGridTiles() {
    let otherActor = Actor(size: Size(width: 100, height: 100))
    game.scene.add(otherActor)

    actor.size = otherActor.size
    actor.position = Point(x: 200, y: 0)

    var numberOfCollisions = 0

    actor.events.collided(with: otherActor).observe {
        numberOfCollisions += 1
    }

    XCTAssertEqual(numberOfCollisions, 0)

    // Actors start intersecting = first collision
    actor.position = Point(x: 0, y: 0)
    XCTAssertEqual(numberOfCollisions, 1)

    // Actor moving out of grid tile and then back into it while
    // in contact the whole time
    actor.position = Point(x: 60, y: 0)
    actor.position = Point(x: 40, y: 0)
    XCTAssertEqual(numberOfCollisions, 1)
}

This is happening because when an actor leaves a tile the other actors in that tile are removed from actorsInContact:

for otherActor in tile.actors {
    guard otherActor.actorsInContact.remove(actor) != nil else {
        continue
    }

    actor.actorsInContact.remove(otherActor)
}

I noticed that nowhere in Grid do we check if two actors that were in contact are no longer in contact.

Is this by design to get better performance or is this a bug?

Texture does not load

Place image in the workspace.
image

Block

let ground = Block(size: size, spriteSheetName: "Ground")
ground.position = center
add(ground)

Player

let player = Actor()
player.position = center
add(player)
player.textureNamePrefix = "Player/"
        
let idleAnimation = Animation(name: "Idle", frameCount: 1, frameDuration: 1)

The ground is visible, but the player is hidden.

Issue with Walkabout Tutorial

I am have been trying to go through the Walkabout tutorial. But I can't seem to get stuck at the first couple of steps.

Repo Steps:

  1. Download Repo
  2. open ImagineEngine/Documentation/Tutorials/2-Walkabout/Walkabout.xcworkspace
  3. Hit Command + B to build
  4. Add to the playground import ImagineEngine

Result: see the following error:
`Playground execution failed:

error: Playground.playground:3:8: error: no such module 'ImagineEngine'
import ImagineEngine`

Expected Result: That it finds the module and doesn't produce the error.

Mac OSX: 10.12.6
Xcode: 9.0.1

Add 'moved' event for camera

Imagine Engine offers an event system to enable users to trigger logic based on when something happened in a scene. For example, here's how an Actor can be observed, triggering a closure whenever it's moved:

actor.events.moved.observe {
    // The actor was moved
}

We should add a similar event to Camera as well, so that you can do this:

camera.events.moved.observe {
    // The camera was moved
}

Things to do:

  • Camera does not yet support events, so a CameraEventCollection will need to be created.
  • Add a moved event (similar to how it works for Actor).
  • Trigger the moved event whenever a camera's position was changed.

Good to know:

  • Each Scene has a Camera (see the camera property).
  • See ActorEventCollection to see how events are defined for Actor.
  • For inspiration on how to unit test this change, see ActorTests, which include several tests that involve events.

Support creating Actors from a shape

In the current implementation all Actors are backed by textures, but it would be nice to be able to create Actors that are just shapes.

Since core animation already has a CAShapeLayer, a new class ShapeLayer could be created to provide a cleaner API. Then the Actor class needs to be changed so that it can use different types of layers.

Support JPG-based textures

Currently, Imagine Engine only supports loading texture images in the PNG format, but it would be very nice to support JPG as well. Suggested implementation is to add a Format enum to Texture, that lets the user specify the format, and then in TextureManager read that format and decide the file extension based on it.

Enable scale to be taken into account when doing collision detection

Currently, scale is not being taken into account when performing collision detection (only the original frame of an actor is used). The task here is to apply an actor's scale to the frame that is used for collision detection, unless a custom hit box has been defined. So if an actor has a size of 50 x 30 and a scale factor of 3, the size of the rectangle used for collision detection should be 150 x 90.

  • Collision detection happens in the Grid class.
  • An actor's rect used for collision detection is computed in rectForCollisionDetection.
  • A custom hit box can be defined through an Actor's hitBoxSize property.

The Timeline API doesn't allow to repeat an action at random intervals

It seems like there's no way to repeat an action at random intervals easily.

This could be useful for example if we want to add an enemy to a scene every n seconds (n being randomly generated every time).

The call site could look like this:

let intervalGenerator: () -> TimeInterval = { TimeInterval(arc4random_uniform(10)) } 
scene.timeline.repeat(withInterval: intervalGenerator) {
    let enemy = Actor()
    enemy.animation = Animation(name: "Enemy", frameCount: 5, frameDuration: 0.15)
    scene.add(enemy)
}

What do you think?

Strange behaviour when repeating RotateAction with a delta

If you try to repeat a RotateAction with a delta the rotation is reset to the original value each time the rotation action repeats. I expected the rotation to continue at a constant rate until cancelled.

Example code:

actor.repeat(RotateAction(delta: 3.14, duration: 1))

I think all that is needed is to add this function to MetricAction:

public override func start(for object: Object) {
    startMetric = nil
}

This is consistent with how MoveAction is implemented.

Tutorials in actual project

Create a tutorial about using ImagineEngine in an actual project with adding assets to a project instead of a folder in a playground.

Take scale into account when evaluating actor scene constraint

We should take an actor's scale into account when evaluating its scene constraint. Constraints enables users to restrict where and how actors can move, and an actor can be prevented from leaving its scene with the following code:

// In a scene
let actor = Actor()
actor.constraints = [.scene]
add(actor)

However, if a scale is set, the actor's original size will be used for the constraint calculations:

actor.size = Size(width: 100, height: 200)
actor.scale = 0.5

The actor will now still be treated as having a physical size of 100 x 200, even though in reality it's 50 x 100. Here's a screenshot from Revazendo that shows the problem:

simulator screen shot - iphone 5s - 2017-11-11 at 13 01 53

Good to know:

  • An actor's constraints are evaluated in the Grid class, in the actorRectDidChange method.
  • An actor's rect is adjusted for its scale, so it should be used instead of its size.

What kind of games are you building?

I'd like to start this issue as a thread to see what kind of games that you are either building, or are looking to build, using Imagine Engine. It would be great to hear in order to decide what directions to take the engine in, and to identify any new features that the community could benefit from πŸš€

So whether you have screenshots, a prototype, or just a loose idea - I'd love to hear it! πŸ˜€ Put it as a comment and we'll break off any discussions about new features or how to implement certain things into separate issues πŸ‘

Game Code Sample

Would be great to provide a (very) simple game demo like the ones Apple provides in the Code Examples to better understand how the engine works.

consider to open a Slack channel

Hey there,

awesome Project, love it so far. Since development happens pretty fast, i recommend using Slack (or an alternative) for faster communication. It has already been proven helpful in other projects (take a look at Vapor for example). It is also a great platform for sharing ideas and providing help.

Make a Scene's layer opaque + set black background color

Currently, a Scene is rendered with transparency, and has a clear background color by default. Then, GameViewController sets a background of its view to black, in order to get a black scene.

This should be changed, so that GameViewController doesn't have to do anything, and instead we should make Scene have a black background color by default, which should also enable us to always render a scene without transparency. This should give us a slight performance boost as Core Animation doesn't have to include alpha in the layer's backing.

Things to do here:

  • Remove the code that sets background colors from GameViewController on both iOS/tvOS + macOS.
  • Make Scene have a black background color per default instead of clear.
  • Enable isOpaque on the scene's layer.

UI/snapshot testing

Some parts of Imagine Engine are a bit tricky to fully test with unit tests. While we are getting to a pretty high coverage now, parts of the rendering code are not really covered by tests, making refactors & fixes slow and risky.

It would be interesting to experiment with UI testing or snapshot testing for some of these cases, where we could - for instance - render an object and assert that the visual outcome matched our expectation.

I think if we can combine this with our current suite of unit tests we will have a quite strong test suite and will be able to move faster with great quality πŸ‘

Game elements visibility in actual project

Somewhat related to #140, this fork is my attempt to get the AsteroidBlaster game from the tutorial running in a standalone macOS app:

https://github.com/lcs-rgordon/ImagineEngine

I've tried to keep my commits atomic so that it's easy to see what I've done:

https://github.com/lcs-rgordon/ImagineEngine/commits/master

Doing my best to follow @JohnSundell advice from Twitter I'm using the AppDelegate to create a GameWindowController instance to launch the game:

https://mobile.twitter.com/johnsundell/status/951739402845384704

When the game window opens, the dark blue background is visible, and I've confirmed using the debugger that ImagineEngine is doing it's thing (for example, I can see that asteroids are spawning every two seconds).

However, the window only shows a dark blue background.

I'm betting I've missed something, but I've opened this issue in case there is something with ImagineEngine that could be fixed.

Forgive me if I'm not reporting this issue correctly, I'm a relative newbie at contributing to open source projects via GitHub.

Remember texture scale fallbacks

As a continuation of #15, we should make an optimization for when Imagine Engine falls back on lower-scale textures, so that it remembers what fallbacks it has made.

The advantage of this optimization is that no additional I/O has to be done when the same texture is requested multiple times.

The implementation could be as simple as a dictionary in TextureManager, that keeps a map between texture names and their highest known scale.

Make Label rotatable

You should be able to rotate a Label using the same API that you can use for actors. Functionality like this is implemented in a protocol oriented fashion, so all that needs to be done is to make Label conform to Rotatable. See Actor's implementation of this protocol for reference.

Line animation issue!

Dear Mr Sundell.
Waiting for the promised line animations, but got blocked. Care to explain please?
//SS

Buggy game example

I just can't run it. I follow instructions for asteroids game and can't understand even how to run it. Playground is compiled, shows some stats to the right and thats everything that works. Where the screen with working game?

In general your engine is not a UIKit control so it is a bad idea to make it as a playground:
1)it takes too much time to compile it every time you change the code
2)99% of people will use this engine in a project, not playground so it is better to see how to integrate it into project/workspace without playgrounds

Best approach for implementing gravity in a platformer?

I'm trying to make a Mario style platformer with Imagine. Nearly everything has been incredibly easy and intuitive so far, kudos! I would love to get some advice on the best approach to implementing gravity. The two approaches you mentioned on Twitter are:

  1. Use vertical velocity to continuously move the player downwards - pretty straightforward
  2. Create a custom action - curious on the best approach here

Any code examples or tips would be greatly appreciated, thanks!

Add 'actorAdded' event on Scene

In order to be able to observe whenever an actor was added to a scene, it would be awesome to have an event for it. That way really cool plugins could be written, like this:

class ColorAllActorsPlugin: Plugin {
    func activate(for scene: Scene, in game: Game) {
        scene.events.actorAdded.observe { scene, actor in
            actor.backgroundColor = .red
        }
    }
}

The above plugin would add a red background color to all actors that are added to the scene that the plugin is attached to. Very useful to implement things like debug tools πŸ‘

Things to do:

  • Add a new event on SceneEventCollection for when an actor was added (see the other events for inspiration).
  • Trigger the event when a new actor is added to a scene.

Removing colliding actor sometimes causes extra collision event

Here is a test that triggers this bug:

func testObservingCollisionWhenCollidingActorIsRemoved() {
    actor.size = Size(width: 100, height: 100)
    actor.position = Point(x: 300, y: 300)

    let otherActor = Actor(size: Size(width: 100, height: 100))
    game.scene.add(otherActor)

    var numberOfCollisions = 0

    actor.events.collided(with: otherActor).observe {
        otherActor.remove()
        numberOfCollisions += 1
    }

    XCTAssertEqual(numberOfCollisions, 0)

    // Move the actor to trigger collision detection but not so far that it
    // collides with the other actor.
    actor.position = Point(x: 200, y: 0)
    // Move the other actor so that it collides with the actor
    otherActor.position = actor.position

    XCTAssertEqual(numberOfCollisions, 1)
}

Note that for this bug to happen the actor that is removed must be the one that triggers the collision.

Doing the following in Grid fixes the bug:

private func detectCollisions(between actor: Actor, and otherActors: Set<Actor>) {
    for otherActor in otherActors {
        guard otherActor !== actor else {
            continue
        }

        guard otherActor.scene != nil else {
            continue
        }

        guard actor.scene != nil else {
            continue
        }

        ...
    }
}

I haven't created a PR yet because even though all tests pass I'm unsure if this can cause other bugs.
What do you thing @JohnSundell ?

Add 'resized' event for Camera

Similar to how Actor has a resized event that gets triggered whenever its size changes, we should do the same thing for Camera. The nice thing is that this would allow a scene to easily be resized whenever its container view gets resized (since the size of the camera is the same as the size of the view).

Things to do:

  • Add a resized event on CameraEventCollection.
  • Trigger the new event whenever a camera's size was changed.

Good to know:

  • Take a look at how the resized event works for Actor.
  • For inspiration on how to test this change, check out ActorTests, which includes many tests for events.

Add pathfinding ability

Hey there,

i noticed an obstacle-texture in the second tutorial. I suppose you allready had the idea of implementing pathfinding in the tutorial. I tried to implement it myself with help from a guide from raywenderlich.com, but it's a bit more complex than i thought. I think pathfinding would give the engine a whole lot more capability and usefullness.

Support retrieving plugins based on type

Imagine Engine supports plugins that can be used to extend the functionality of its core objects, such as Actor, Scene and Camera. All objects that can have plugins attached to them conform to the Pluggable protocol.

An API should be added on Pluggable that enables the user to retrieve plugins based on a type, so you are able to do this:

let actor = Actor()
actor.add(MyPlugin())

let plugins = actor.plugins(ofType: MyPlugin.self)

Good to know:

  • Pluggable defines the API for managing plugins.
  • Pluggable already has a removePlugins(ofType:) API that can be used for inspiration.
  • Internally, a PluginManager class is used by all objects to manage their plugins.

Add option to warn if texture is missing when in debug mode

We should make it easier to identify why a texture is not rendered on the screen. In TextureManager, when an image for a texture couldn't be found, we should make it possible to either log an error to the console or to trigger an assert. This should be off by default but be configurable by the user:

Things to do:

  • Make it possible to toggle an errorMode on TextureManager (for example using an enum) that lets the user pick between no error, triggering an assert or logging an error if a texture is missing.
  • In TextureManager, when a texture can't be found, read the error mode and act accordingly.
  • This should only be evaluated in debug builds.

Good to know:

  • All the texture loading code takes place in TextureManager.
  • The default behavior is currently to silently return nil when a texture is missing, this is what should optionally be changed.

Add convenience API to add multiple objects to a scene at once

It would be nice to have a convenience API that lets the user add multiple actors, blocks or labels to a scene at once. So that you can write code like this:

let actorA = Actor()
let actorB = Actor()
let actorC = Actor()
scene.add(actorA, actorB, actorC)

let blockA = Block(size: .zero, textureCollectionName: "")
let blockB = Block(size: .zero, textureCollectionName: "")
let blockC = Block(size: .zero, textureCollectionName: "")
scene.add(blockA, blockB, blockC)

let labelA = Label()
let labelB = Label()
let labelC = Label()
scene.add(labelA, labelB, labelC)

The convenience API can be added as an extension on Scene πŸ‘

Dialogue Plugin

Hey there! I'm trying to create a plugin that handles dialogues. I could use some help :D
The final goal is a plugin that can be added on any kinda games. Something simple like a sign in a village or even a complex dialogue with characters.

I’ve two games (dialogue system or something like that) in mind:

Fire Emblem Heroes
uspiuus

  • Traditional RPG style (character is showed while talking)
  • Character animation (the character usually changes his pose/posture/sprite)

Undertale
screen shot 2018-01-11 at 18 24 13

  • Simple
  • Animated words (single and all the text) (Label supports animation now o/)

I tried to play with those in mind on a Playground. What I got so far:

  • Reading a local JSON (Text, Character (name), Animation (Character sprite))
  • Update method that call the next item in array (JSON). Everything is working so far.

screen shot 2018-01-11 at 16 58 23

screen shot 2018-01-11 at 18 49 38

(Raven of Fire Emblem. Testing)

Next:

  • Multiline (I was using \n on JSON haha) (HIGH PRIORITY!)
  • Simple animations (Label and Actor already got a lot what I need like fade out/in and so on)
  • Typing effect (as an option)
  • Adapt the plugin to be either simple (only a box n text), normal (name + dialogue) and dialogue (character sprite) and for any screen size

This is it! I’m a beginner and I don’t know if I can create something like that using clean and elegant code or best practices at all (But I’m learning. Reading Imagine Engine is helping me a lot). I could use some direction or tips. Thanks and sorry for all English mistake :D

Extract collision detection logic from Actor

To be able to add new types of SceneObjects that support collision detection without duplicating code, the collision detection logic in Actor needs to be extracted.

We probably need a new protocol something like this:

internal protocol Collidable {
    var isCollisionDetectionEnabled: Bool { get set }
    var rectForCollisionDetection: Rect { get }
}

Collision events also need to be extracted from ActorEventCollection.

Support easy loading of textures from an asset catalog

Since Imagine Engine doesn't rely on the default system image loading mechanism (using UIImage(named:) on iOS) loading textures from an asset catalog is not super easy. While possible it requires you to create Animation or Texture instances using Image, like this:

let image = Image(named: "player")!
actor.animation = Animation(image: image)

It would be nicer if Imagine Engine could automatically search the app's asset catalog for an image with a given name, so that you could simply use:

actor.animation = Animation(textureNamed: "player")

Fallback to lower scale textures if exact match can't be found

In Imagine Engine, images for textures are loaded as CGImage directly (not as UIImage or NSImage, for performance reasons). This means that we don't get the feature of automatically falling back to images of a lower scale if an exact match can't be found.

For example, when loading a UIImage, if I specify UIImage(named: "myImage") when running on a @3x device, the system will first try to load [email protected], then [email protected], and so on. We should make Imagine Engine match this behavior.

The texture loading code can be found in TextureManager.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.