Update to Swift 4.2

This commit is contained in:
Christian Treffs 2018-06-07 11:16:54 -07:00
parent cddb155c5a
commit 9b4fc5b206
8 changed files with 126 additions and 198 deletions

View File

@ -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

9
Gemfile Normal file
View File

@ -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"

23
Gemfile.lock Normal file
View File

@ -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

View File

@ -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
View File

@ -1,158 +1,3 @@
# Fireblade ECS (Entity-Component-System)
[![version 0.3.0](https://img.shields.io/badge/version-0.4.0-brightgreen.svg)](releases/tag/v0.4.0)
[![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE)
[![swift version](https://img.shields.io/badge/swift-4-brightgreen.svg)](#)
[![platforms](https://img.shields.io/badge/platform-macOS%20|%20iOS%20|%20linux-brightgreen.svg)](#)
# Fireblade<ProjectName>
This is a **dependency free**, **lightweight**, **fast** and **easy to use** [Entity-Component-System](https://en.wikipedia.org/wiki/Entitycomponentsystem) 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.

View File

@ -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)
}
}

20
generateXcodeProject.sh Executable file
View File

@ -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

26
prepareXcodeProject.rb Executable file
View File

@ -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()