Skip to content

Commit

Permalink
fix: properly break up pieces
Browse files Browse the repository at this point in the history
  • Loading branch information
literalpie committed Aug 23, 2020
1 parent 5ccc651 commit 090aba5
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 29 deletions.
10 changes: 0 additions & 10 deletions Documentation/TODO.md

This file was deleted.

2 changes: 0 additions & 2 deletions Wonky Blocks.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
/* Begin PBXFileReference section */
45332F4C24ECA5D900F7A72C /* preview.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = preview.gif; sourceTree = "<group>"; };
453EDD6124998982006470E7 /* Joystick.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Joystick.swift; sourceTree = "<group>"; };
453FBC7A249ADEB20056E4D7 /* TODO.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = TODO.md; sourceTree = "<group>"; };
45525A65244C7FD4004974AB /* Wonky Blocks.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Wonky Blocks.app"; sourceTree = BUILT_PRODUCTS_DIR; };
45525A68244C7FD4004974AB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
45525A6A244C7FD4004974AB /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -89,7 +88,6 @@
45332F4924ECA14500F7A72C /* Documentation */ = {
isa = PBXGroup;
children = (
453FBC7A249ADEB20056E4D7 /* TODO.md */,
45332F4C24ECA5D900F7A72C /* preview.gif */,
);
path = Documentation;
Expand Down
5 changes: 3 additions & 2 deletions Wonky Blocks/Support/extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ extension CGPath {

extension SKNode {
// Gets the paths of all children, positioned based on the nodes position
var childrenPositionPaths: [CGPath] {
var childrenPositionPaths: [(path: CGPath, center: CGPoint)] {
return self.children.compactMap{ child in
if let childPath = (child as? SKShapeNode)?.path,
let center = child.userData?.value(forKey: "center") as? CGPoint,
let transformedPath = childPath.rotateAroundCenter(path: childPath, float: self.zRotation) {
var translateTransform = CGAffineTransform(translationX: self.position.x, y: self.position.y)

return transformedPath.copy(using: &translateTransform)
return ((path: transformedPath.copy(using: &translateTransform), center: center) as! (path: CGPath, center: CGPoint))
} else {
return nil
}
Expand Down
68 changes: 57 additions & 11 deletions Wonky Blocks/UI/GameViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import UIKit
import SpriteKit
import Combine
import SwiftClipper

class WonkyGameViewController: UIViewController {
var spriteKitView: SKView {
Expand Down Expand Up @@ -159,8 +160,8 @@ class WonkyGameViewController: UIViewController {
contactingBodies.forEach({ (intersectingNode) in
// This will go through each of the bodies that intersects the row being removed.
// collect the paths that will make up the piece above and below the removed row.
var belowPaths: [CGPath] = []
var abovePaths: [CGPath] = []
var belowPaths: [(path: Path, center: CGPoint)] = []
var abovePaths: [(path: Path, center: CGPoint)] = []

// One square of the tetronimo
intersectingNode.childrenPositionPaths.forEach { intChildPath in
Expand All @@ -169,31 +170,76 @@ class WonkyGameViewController: UIViewController {
var rowTranslateTransform = CGAffineTransform(translationX: fromRow.position.x, y: fromRow.position.y)
let transformedRowPath = fromRow.path?.copy(using: &rowTranslateTransform)

let difference = intChildPath.getPathElementsPoints().difference((transformedRowPath!.getPathElementsPoints()))
let difference = intChildPath.path.getPathElementsPoints().difference((transformedRowPath!.getPathElementsPoints()))
difference.forEach({ (differencePiece) in
// Area only seems to be accurate when the points of the path are clockwise.
// If the remaining are of the piece is very small (less than 50 area), we will remove it completely.
if differencePiece.asClockwise().area > 50 || differencePiece.asClockwise().area < -50 {
if differencePiece.first!.y > fromRow.position.y {
abovePaths.append(differencePiece.asCgPath())
abovePaths.append((differencePiece, intChildPath.center))
} else {
belowPaths.append(differencePiece.asCgPath())
belowPaths.append((differencePiece, intChildPath.center))
}
}
})
}
if !abovePaths.isEmpty {
let newNode = WonkyTetronimo(with: abovePaths)
resultNodes.append(newNode)
newNode.physicsBody?.affectedByGravity = true
let fragments = groupFragments(from: abovePaths)
fragments.forEach { (fragment) in
let newNode = WonkyTetronimo(with: fragment)
resultNodes.append(newNode)
newNode.physicsBody?.affectedByGravity = true
}
}
if !belowPaths.isEmpty {
let newNodeBelow = WonkyTetronimo(with: belowPaths)
resultNodes.append(newNodeBelow)
newNodeBelow.physicsBody?.affectedByGravity = true
let fragments = groupFragments(from: belowPaths)
fragments.forEach { (fragment) in
let newNode = WonkyTetronimo(with: fragment)
resultNodes.append(newNode)
newNode.physicsBody?.affectedByGravity = true
}
}
breakingNodes.append(intersectingNode)
})
return (resultNodes, breakingNodes)
}

/// given some shape paths, return the same paths with touching shapes grouped together.
func groupFragments(from paths: [(path: SwiftClipper.Path, center: CGPoint)]) -> [[(path: SwiftClipper.Path, center: CGPoint)]] {
/// each element is a group of paths, and the node's original center (used to determine connection)
var allPathGroups: [[(path: SwiftClipper.Path, center: CGPoint)]] = []
paths.forEach { path in
let touchingPieces = allPathGroups.enumerated().filter { existingPath in
let matchingPaths = existingPath.element.filter { existingPathPiece in

let distance = path.center.distance(to: existingPathPiece.center)
return distance < 60
}
// print(matchingPaths.count, path.center)
return existingPath.element.first { existingPathPiece in

let distance = path.center.distance(to: existingPathPiece.center)
print(distance)
return distance < 60
} != nil
}
print("touching pieces? \(touchingPieces.count)")
if touchingPieces.count > 1 {
// Some pieces were previously put in separate groups, but this new path connects them.
if touchingPieces.count > 2 {
print("warning, more than 2 touching pieces, but only 2 will be merged")
}
let pieceToMerge = allPathGroups.remove(at: touchingPieces[1].offset)
allPathGroups[touchingPieces[0].offset].append(contentsOf: pieceToMerge)
allPathGroups[touchingPieces[0].offset].append(path)
} else if let touchingPiece = touchingPieces.first?.offset {
// path is touching a path within touchingPiece, so add it to the same piece.
allPathGroups[touchingPiece].append(path)
} else {
// path isn't touching any of the existing pieces, so make a new one with this new path.
allPathGroups.append([path])
}
}
return allPathGroups
}
}
2 changes: 1 addition & 1 deletion Wonky Blocks/UI/SKNodes/WonkyRow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class WonkyRow: SKShapeNode {
// take the sum of the area of the intersection between every piece and the row.
var totalArea = soFar
current.node?.childrenPositionPaths.forEach { childNodePath in
let intersect = self.path?.getPathElementsPoints().intersection(childNodePath.getPathElementsPoints())
let intersect = self.path?.getPathElementsPoints().intersection(childNodePath.path.getPathElementsPoints())
intersect?.forEach({ (path) in
totalArea += -path.asClockwise().area
})
Expand Down
12 changes: 9 additions & 3 deletions Wonky Blocks/UI/SKNodes/WonkyTetronimo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//

import SpriteKit
import SwiftClipper

class WonkyTetronimo: SKShapeNode {
var center: CGPoint {
Expand All @@ -33,6 +34,8 @@ class WonkyTetronimo: SKShapeNode {
// unfortunately, this also causes things to "catch" on each other occasionally.
let path = CGPath(rect: CGRect(x: row.offset * 50, y: col.offset * 50, width: 48, height: 48), transform: nil)
let square = SKShapeNode(path: path)
square.userData = NSMutableDictionary()
square.userData?.setValue(path.getPathElementsPoints().asClockwise().centroid, forKey: "center")
square.lineWidth = 1
square.fillColor = .blue
square.strokeColor = .clear
Expand All @@ -48,14 +51,17 @@ class WonkyTetronimo: SKShapeNode {
}

/// creates a tetronimo from a collection of paths. Used to create a tetronimo that replaces a piece that's been chopped up by a removed line.
convenience init(with childrenPaths: [CGPath]) {
/// the orignal center point of each path is also saved into the userData of the nodes
convenience init(with childrenPaths: [(path: Path, center: CGPoint)]) {
self.init()
let children = childrenPaths.compactMap{(path) -> (SKPhysicsBody, SKShapeNode)? in
guard let physicsBody = SKPhysicsBody(polygonFrom: path) as SKPhysicsBody? else {
guard let physicsBody = SKPhysicsBody(polygonFrom: path.path.asCgPath()) as SKPhysicsBody? else {
// sometimes physics bodies fail to create from paths - we don't seem to miss any important nodes because of this.
return nil
}
let diffNode = SKShapeNode(path: path)
let diffNode = SKShapeNode(path: path.path.asCgPath())
diffNode.userData = NSMutableDictionary()
diffNode.userData?.setValue(path.path.asClockwise().centroid, forKey: "center")
diffNode.fillColor = .blue
diffNode.lineWidth = 1
diffNode.strokeColor = .clear
Expand Down

1 comment on commit 090aba5

@literalpie
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Closes #2

Please sign in to comment.