Update to Swift 4.2
This commit is contained in:
parent
cddb155c5a
commit
9b4fc5b206
|
|
@ -2,12 +2,14 @@ included:
|
|||
- Sources
|
||||
excluded:
|
||||
- Tests
|
||||
identifier_name:
|
||||
excluded:
|
||||
- id
|
||||
#identifier_name:
|
||||
# excluded:
|
||||
# - id
|
||||
line_length: 220
|
||||
number_separator:
|
||||
minimum_length: 5
|
||||
disabled_rules:
|
||||
- identifier_name
|
||||
opt_in_rules:
|
||||
- file_header
|
||||
- array_init
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
||||
|
||||
# gem "rails"
|
||||
|
||||
gem "xcodeproj", "~> 1.5"
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.0)
|
||||
atomos (0.1.2)
|
||||
claide (1.0.2)
|
||||
colored2 (3.1.2)
|
||||
nanaimo (0.2.5)
|
||||
xcodeproj (1.5.9)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.2)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.2.5)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
xcodeproj (~> 1.5)
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.1
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// swift-tools-version:4.1
|
||||
// swift-tools-version:4.2
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
|
|
|||
159
README.md
159
README.md
|
|
@ -1,158 +1,3 @@
|
|||
# Fireblade ECS (Entity-Component-System)
|
||||
[](releases/tag/v0.4.0)
|
||||
[](LICENSE)
|
||||
[](#)
|
||||
[](#)
|
||||
# Fireblade<ProjectName>
|
||||
|
||||
This is a **dependency free**, **lightweight**, **fast** and **easy to use** [Entity-Component-System](https://en.wikipedia.org/wiki/Entity–component–system) implementation in Swift. It is developed and maintained as part of the [Fireblade Game Engine project](https://github.com/fireblade-engine).
|
||||
|
||||
See the [Fireblade ECS Demo App](https://github.com/fireblade-engine/ecs-demo) to get started.
|
||||
|
||||
## Getting Started
|
||||
|
||||
These instructions will get you a copy of the project up and running on your local machine and provide a code example.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
* [Swift Package Manager (SPM)](https://github.com/apple/swift-package-manager)
|
||||
* [Swiftlint](https://github.com/realm/SwiftLint) for linting - (optional)
|
||||
* [Jazzy](https://github.com/realm/jazzy) for documentation - (optional)
|
||||
|
||||
### Installing
|
||||
|
||||
Fireblade ECS is available for all platforms that support [Swift 4](https://swift.org/) and the [Swift Package Manager (SPM)](https://github.com/apple/swift-package-manager).
|
||||
|
||||
Extend the following lines in your `Package.swift` file or use it to create a new project.
|
||||
|
||||
```swift
|
||||
// swift-tools-version:4.0
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "YourPackageName",
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/fireblade-engine/ecs.git", from: "0.4.0")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "YourTargetName",
|
||||
dependencies: ["FirebladeECS"])
|
||||
]
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
## Code Example
|
||||
|
||||
<!--Show what the library does as concisely as possible, developers should be able to figure out **how** your project solves their problem by looking at the code example. Make sure the API you are showing off is obvious, and that your code is short and concise.-->
|
||||
|
||||
A core element in the Fireblade-ECS is the [Nexus](https://en.wiktionary.org/wiki/nexus#Noun).
|
||||
It acts as a centralized way to store, access and manage entities and their components.
|
||||
A single `Nexus` may hold up to 4294967295 `Entities` at a time.
|
||||
You may use more than one `Nexus`.
|
||||
|
||||
Initialize a nexus with
|
||||
|
||||
```swift
|
||||
let nexus = Nexus()
|
||||
```
|
||||
|
||||
then create entities by letting the `Nexus` generate them.
|
||||
|
||||
```swift
|
||||
let myEntity = nexus.create(entity: "myEntity")
|
||||
```
|
||||
|
||||
You can define `Components` like this
|
||||
|
||||
```swift
|
||||
class Movement: Component {
|
||||
var position: (x: Double, y: Double) = (0.0, 1.0)
|
||||
var velocity: Double = 0.1
|
||||
}
|
||||
```
|
||||
and assign instances of them to an `Entity` with
|
||||
|
||||
```swift
|
||||
myEntity.assign(Movement())
|
||||
```
|
||||
|
||||
This ECS uses a grouping approach for entities with the same component types to optimize and ease up access to these.
|
||||
Entities with the same component types may be accessed via a so called family.
|
||||
A family has entities as members and component types as family traits.
|
||||
|
||||
Create a family by calling `.family` with a set of traits on the nexus.
|
||||
A family that containts only entities with a `Movement` and `PlayerInput` component, but no `Texture` component is created by
|
||||
|
||||
```swift
|
||||
let family = nexus.family(requiresAll: [Movement.self, PlayerInput.self],
|
||||
excludesAll: [Texture.self],
|
||||
needsAtLeastOne: [Name.self])
|
||||
```
|
||||
|
||||
These entities are cached in the nexus for efficient access and iteration.
|
||||
Iterate family members by calling `.iterate` on the family you want to iterate over.
|
||||
`iterate` provides a closure whose parameters start with the entity identifier (entityId) of the current entity,
|
||||
followed by the typesafe component instances of the current entity that you may provide in your desired order.
|
||||
|
||||
```swift
|
||||
class PlayerMovementSystem {
|
||||
let family = nexus.family(requiresAll: [Movement.self, PlayerInput.self],
|
||||
excludesAll: [Texture.self],
|
||||
needsAtLeastOne: [Name.self])
|
||||
|
||||
func update() {
|
||||
family.iterate { (mov: Movement!, input: PlayerInput!, name: Name?) in
|
||||
|
||||
// position & velocity for the current entity
|
||||
// we know that we will have this component so we force unwrap the component
|
||||
// instance parameter already for easy handling inside the closure
|
||||
|
||||
// get properties
|
||||
_ = mov.position
|
||||
_ = mov.velocity
|
||||
|
||||
// set properties
|
||||
mov.position.x = mov.position.x + 3.0
|
||||
...
|
||||
|
||||
// current input command for the given entity
|
||||
_ = input.command
|
||||
...
|
||||
|
||||
// optional name component that may or may not be part of the current entity
|
||||
_ = name?.name
|
||||
...
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
See the [Fireblade ECS Demo App](https://github.com/fireblade-engine/ecs-demo) to get started.
|
||||
|
||||
<!--## Contributing
|
||||
|
||||
Please read [CONTRIBUTING.md](https://gist.github.com/PurpleBooth/b24679402957c63ec426) for details on our code of conduct, and the process for submitting pull requests to us.-->
|
||||
|
||||
## Versioning
|
||||
|
||||
We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](tags).
|
||||
|
||||
## Authors
|
||||
|
||||
* [Christian Treffs](https://github.com/ctreffs) - *Initial work*
|
||||
* [Manuel Weidmann](https://github.com/vyo)
|
||||
|
||||
See also the list of [contributors](project/contributors) who participated in this project.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
Inspired by
|
||||
- [Ashley](https://github.com/libgdx/ashley)
|
||||
- [Entitas](https://github.com/sschmid/Entitas-CSharp)
|
||||
- [EntitasKit](https://github.com/mzaks/EntitasKit)
|
||||
A description of this package.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,14 @@
|
|||
// Created by Christian Treffs on 16.10.17.
|
||||
//
|
||||
|
||||
#if arch(x86_64) || arch(arm64) // 64 bit
|
||||
private let fibA: UInt = 0x9e3779b97f4a7c15 // = 11400714819323198485 aka Fibonacci Hash a value for 2^64; calculate by: 2^64 / (golden ratio)
|
||||
#elseif arch(i386) || arch(arm) // 32 bit
|
||||
private let fibA: UInt = 0x9e3779b9 // = 2654435769 aka Fibonacci Hash a value for 2^32; calculate by: 2^32 / (golden ratio)
|
||||
#else
|
||||
#error("unsupported architecture")
|
||||
#endif
|
||||
|
||||
// MARK: - hash combine
|
||||
/// Calculates the combined hash of two values. This implementation is based on boost::hash_combine.
|
||||
/// Will always produce the same result for the same combination of seed and value during the single run of a program.
|
||||
|
|
@ -14,26 +22,21 @@
|
|||
/// - value: value to be combined with seed hash.
|
||||
/// - Returns: combined hash value.
|
||||
public func hash(combine seed: Int, _ value: Int) -> Int {
|
||||
/// http://www.boost.org/doc/libs/1_65_1/doc/html/hash/combine.html
|
||||
/// http://www.boost.org/doc/libs/1_65_1/doc/html/hash/reference.html#boost.hash_combine
|
||||
/// http://www.boost.org/doc/libs/1_65_1/boost/functional/hash/hash.hpp
|
||||
/// http://book.huihoo.com/data-structures-and-algorithms-with-object-oriented-design-patterns-in-c++/html/page214.html
|
||||
/// https://stackoverflow.com/a/35991300
|
||||
/// https://stackoverflow.com/a/4948967
|
||||
/*
|
||||
let phi = (1.0 + sqrt(5.0)) / 2 // golden ratio
|
||||
let a32 = pow(2.0,32.0) / phi
|
||||
let a64 = pow(2.0,64.0) / phi
|
||||
*/
|
||||
#if arch(x86_64) || arch(arm64) // 64 bit
|
||||
let fibA: UInt = 0x9e3779b97f4a7c15 // = 11400714819323198485 aka Fibonacci Hash a value for 2^64; calculate by: 2^64 / (golden ratio)
|
||||
#elseif arch(i386) || arch(arm) // 32 bit
|
||||
let fibA: UInt = 0x9e3779b9 // = 2654435769 aka Fibonacci Hash a value for 2^32; calculate by: 2^32 / (golden ratio)
|
||||
#endif
|
||||
/// http://www.boost.org/doc/libs/1_65_1/doc/html/hash/combine.html
|
||||
/// http://www.boost.org/doc/libs/1_65_1/doc/html/hash/reference.html#boost.hash_combine
|
||||
/// http://www.boost.org/doc/libs/1_65_1/boost/functional/hash/hash.hpp
|
||||
/// http://book.huihoo.com/data-structures-and-algorithms-with-object-oriented-design-patterns-in-c++/html/page214.html
|
||||
/// https://stackoverflow.com/a/35991300
|
||||
/// https://stackoverflow.com/a/4948967
|
||||
/*
|
||||
let phi = (1.0 + sqrt(5.0)) / 2 // golden ratio
|
||||
let a32 = pow(2.0,32.0) / phi
|
||||
let a64 = pow(2.0,64.0) / phi
|
||||
*/
|
||||
var uSeed: UInt = UInt(bitPattern: seed)
|
||||
let uValue: UInt = UInt(bitPattern: value)
|
||||
uSeed ^= uValue &+ fibA &+ (uSeed << 6) &+ (uSeed >> 2)
|
||||
return Int(bitPattern: uSeed)
|
||||
let uValue: UInt = UInt(bitPattern: value)
|
||||
uSeed ^= uValue &+ fibA &+ (uSeed << 6) &+ (uSeed >> 2)
|
||||
return Int(bitPattern: uSeed)
|
||||
}
|
||||
|
||||
/// Calculates the combined hash value of the elements. This implementation is based on boost::hash_range.
|
||||
|
|
@ -41,8 +44,8 @@ public func hash(combine seed: Int, _ value: Int) -> Int {
|
|||
/// - Parameter hashValues: sequence of hash values to combine.
|
||||
/// - Returns: combined hash value.
|
||||
public func hash<S: Sequence>(combine hashValues: S) -> Int where S.Element == Int {
|
||||
/// http://www.boost.org/doc/libs/1_65_1/doc/html/hash/reference.html#boost.hash_range_idp517643120
|
||||
return hashValues.reduce(0) { hash(combine: $0, $1) }
|
||||
/// http://www.boost.org/doc/libs/1_65_1/doc/html/hash/reference.html#boost.hash_range_idp517643120
|
||||
return hashValues.reduce(0) { hash(combine: $0, $1) }
|
||||
}
|
||||
|
||||
/// Calculates the combined hash value of the elements. This implementation is based on boost::hash_range.
|
||||
|
|
@ -50,28 +53,28 @@ public func hash<S: Sequence>(combine hashValues: S) -> Int where S.Element == I
|
|||
/// - Parameter hashValues: sequence of hash values to combine.
|
||||
/// - Returns: combined hash value.
|
||||
public func hash<H: Sequence>(combine hashables: H) -> Int where H.Element == Hashable {
|
||||
/// http://www.boost.org/doc/libs/1_65_1/doc/html/hash/reference.html#boost.hash_range_idp517643120
|
||||
return hashables.reduce(0) { hash(combine: $0, $1.hashValue) }
|
||||
/// http://www.boost.org/doc/libs/1_65_1/doc/html/hash/reference.html#boost.hash_range_idp517643120
|
||||
return hashables.reduce(0) { hash(combine: $0, $1.hashValue) }
|
||||
}
|
||||
|
||||
// MARK: - entity component hash
|
||||
extension EntityComponentHash {
|
||||
|
||||
static func compose(entityId: EntityIdentifier, componentTypeHash: ComponentTypeHash) -> EntityComponentHash {
|
||||
let entityIdSwapped: UInt = UInt(entityId).byteSwapped // needs to be 64 bit
|
||||
let componentTypeHashUInt: UInt = UInt(bitPattern: componentTypeHash)
|
||||
let hashUInt: UInt = componentTypeHashUInt ^ entityIdSwapped
|
||||
return Int(bitPattern: hashUInt)
|
||||
}
|
||||
static func compose(entityId: EntityIdentifier, componentTypeHash: ComponentTypeHash) -> EntityComponentHash {
|
||||
let entityIdSwapped: UInt = UInt(entityId).byteSwapped // needs to be 64 bit
|
||||
let componentTypeHashUInt: UInt = UInt(bitPattern: componentTypeHash)
|
||||
let hashUInt: UInt = componentTypeHashUInt ^ entityIdSwapped
|
||||
return Int(bitPattern: hashUInt)
|
||||
}
|
||||
|
||||
static func decompose(_ hash: EntityComponentHash, with entityId: EntityIdentifier) -> ComponentTypeHash {
|
||||
let entityIdSwapped: UInt = UInt(entityId).byteSwapped
|
||||
static func decompose(_ hash: EntityComponentHash, with entityId: EntityIdentifier) -> ComponentTypeHash {
|
||||
let entityIdSwapped: UInt = UInt(entityId).byteSwapped
|
||||
let entityIdSwappedInt: Int = Int(bitPattern: entityIdSwapped)
|
||||
return hash ^ entityIdSwappedInt
|
||||
}
|
||||
return hash ^ entityIdSwappedInt
|
||||
}
|
||||
|
||||
static func decompose(_ hash: EntityComponentHash, with componentTypeHash: ComponentTypeHash) -> EntityIdentifier {
|
||||
let entityId: Int = (hash ^ componentTypeHash).byteSwapped
|
||||
return EntityIdentifier(truncatingIfNeeded: entityId)
|
||||
}
|
||||
static func decompose(_ hash: EntityComponentHash, with componentTypeHash: ComponentTypeHash) -> EntityIdentifier {
|
||||
let entityId: Int = (hash ^ componentTypeHash).byteSwapped
|
||||
return EntityIdentifier(truncatingIfNeeded: entityId)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# clean swift package
|
||||
swift package clean
|
||||
rm -rdf .build/
|
||||
|
||||
# install dependencies
|
||||
bundle install
|
||||
swift package update
|
||||
|
||||
# generate project
|
||||
swift package generate-xcodeproj #--xcconfig-overrides settings.xcconfig
|
||||
|
||||
# add project specialities
|
||||
bundle exec ./prepareXcodeProject.rb
|
||||
|
||||
# open project
|
||||
open *.xcodeproj
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
require 'xcodeproj'
|
||||
|
||||
path_to_project = Dir.glob('*.xcodeproj').first
|
||||
|
||||
project = Xcodeproj::Project.open(path_to_project)
|
||||
|
||||
# add build phases
|
||||
project.targets.each do |target|
|
||||
|
||||
puts "Add Run Shell Script Phase: Swiftlint to #{target}"
|
||||
swiftlint_phase = target.new_shell_script_build_phase("Run Swiftlint")
|
||||
swiftlint_phase.shell_script = """
|
||||
set -e
|
||||
if which swiftlint >/dev/null; then
|
||||
swiftlint autocorrect
|
||||
swiftlint
|
||||
else
|
||||
echo 'warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint'
|
||||
fi
|
||||
"""
|
||||
|
||||
end
|
||||
|
||||
project.save()
|
||||
Loading…
Reference in New Issue