I've started doing a small swift / spritekit project to teach myself game dev. It starts with an isometric map, which I managed to draw. But I'm having trouble getting a precise touch location on the different tiles of the map. It works, but is slightly out of place, and does not seem consistent.
Here are my functions :
class PlayScene: SKScene {
let map = SKNode()
override func didMoveToView(view: SKView) {
let origin = view.frame.origin
let mapOrigin = CGPointMake(origin.x + self.frame.width / 4, origin.y - self.frame.height / 4)
let mapConfig: Int[][] =
[[0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0],
[2, 2, 2, 2, 1, 2, 2, 2],
[0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0]]
drawMap(mapConfig, mapOrigin: mapOrigin)
}
With :
func drawMap(mapConfig:Int[][], mapOrigin:CGPoint)
{
let tileHeight:CGFloat = 25.5
let numColumns:Int = 8
let numRows:Int = 8
var position = mapOrigin
var column: Int = 0
var row: Int = 0
for column = 0; column < numColumns; column++
{
for row = 0; row < numRows; row++
{
position.x = mapOrigin.x + CGFloat(column) * tileHeight
position.y = mapOrigin.y + CGFloat(row) * tileHeight
let isoPosition = twoDToIso(position)
placeTile(isoPosition, mapConfig: mapConfig[row][column])
}
}
self.addChild(map)
}
func placeTile(position:CGPoint, mapConfig:Int)
{
switch mapConfig
{
case 0:
let sprite = SKSpriteNode(imageNamed:"grassTile")
sprite.position = position
sprite.setScale(0.1)
sprite.name = "\(position)"
self.map.addChild(sprite)
case 1:
let sprite = SKSpriteNode(imageNamed:"roadTile")
sprite.position = position
sprite.setScale(0.1)
sprite.name = "\(position)"
self.map.addChild(sprite)
default:
let sprite = SKSpriteNode(imageNamed:"roadTileLTR")
sprite.position = position
sprite.setScale(0.1)
sprite.name = "\(position)"
self.map.addChild(sprite)
}
}
And then I want to hide the tile I touch (for testing) :
override func touchesBegan(touches: NSSet, withEvent event: UIEvent)
{
for touch: AnyObject in touches
{
let locationNode = touch.locationInNode(self)
nodeAtPoint(locationNode).hidden = true
}
}
But it does not always hide the correct tile. So how should I tackle this ? Is my code fundamentally wrong (possible) ? Or do I need to convert the location to iso coordinates in some way ? Or play with the tiles bitmask ?
Thanks for your help in any case !
I had a similar issue with isometric map.
The problem is that the node you click on is bigger than displayed (it has transparent parts). See my question here for a better explanation of the issue.
Here is how I solved it (sorry code is in Objective-C) :
1.Create a CGPathRef following the edges of the tile (tileSize being the size of the texture). This code is for regular isometric tiles, not hexagonal but the idea is the same.
// ObjC
-(CGPathRef)createTextureFrame:(CGSize)tileSize
{
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 0, -(self.tileSize.height / 2));
CGPathAddLineToPoint(path, NULL, (self.tileSize.width / 2), 0);
CGPathAddLineToPoint(path, NULL, 0, (self.tileSize.height / 2));
CGPathAddLineToPoint(path, NULL, -(self.tileSize.width / 2), 0);
CGPathCloseSubpath(path);
return path;
}
// Swift
func createTextureFrame(tileSize:CGSize) -> CGPathRef {
CGMutablePathRef path = CGPathCreateMutable()
CGPathMoveToPoint(path, nil, 0, -(self.tileSize.height / 2))
CGPathAddLineToPoint(path, nil, (self.tileSize.width / 2), 0)
CGPathAddLineToPoint(path, nil, 0, (self.tileSize.height / 2))
CGPathAddLineToPoint(path, nil, -(self.tileSize.width / 2), 0)
CGPathCloseSubpath(path)
return path
}
2.Create a function that checks if a given point is inside the CGPathRef (textureFrame).
// ObjC
-(BOOL)isPointOnNode:(CGPoint)point
{
return CGPathContainsPoint(textureFrame, NULL, point, YES);
}
// Swift
func isPointOnNode(point:CGPoint) -> Bool {
return CGPathContainsPoint(textureFrame, nil, point, YES)
}
3.For each touched nodes, check which textureFrame we're in.
// ObjC
UITouch *touch = [touches anyObject];
NSArray *nodes = [self nodesAtPoint:[touch locationInNode:self]];
for (SKNode *node in nodes)
{
CGPoint locationInNode = [touch locationInNode:node];
if ([node isPointOnNode:locationInNode])
{
node.hidden = YES;
}
}
// Swift
var touch = touches.anyObject() as UITouch
var nodes = self.nodesAtPoint(touch.locationInNode(self))
for node in nodes as [SKNode] {
var locationInNode = touch.locationInNode(node)
if node.isPointOnNode() {
node.hidden = true
}
}
I do not know if it's the best solution but it works great. I hope it helps :)
Edit : Added Swift version of the code
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With