I want to randomly generate the position for a sprite. I use the arc4random() method to get a range from 1 to 8.
var randomFactor = arc4random_uniform(8) + 1
sprite.position = CGPointMake(self.frame.width/8 * randomFactor, self.frame.height)
An error pops up telling me "UIInt32 is not convertible to UIInt8".
Is there a way to get around this or design the code differently to make it work? Like a solution where the random method isn't a UIInt32 but a UIInt8.
Thanks in advance.
You should convert 'randomFactor' to a CGFloat:
var randomFactor = arc4random_uniform(8) + 1
var x = self.frame.width / 8 * CGFloat(randomFactor)
var y = self.frame.height
var position = CGPointMake(x, y)
Related
I'm currently making a game with Spritekit and struggling to figure out why my normalized Textures are not being applied properly when rendering on device, while they seem to be fine in the simulator.
Here is the code that adds the normal textures to the tile definitions, among other things:
self.wallTileMap = self.scene?.childNode(withName: "Walls") as? SKTileMapNode
let textureAtlas = SKTextureAtlas(named: "Wall Normal Maps")
if let tileMap = self.wallTileMap {
let startingLocation:CGPoint = tileMap.position
let tileSize = tileMap.tileSize
let halfWidth = CGFloat(tileMap.numberOfColumns) / 2.0 * tileSize.width
let halfHeight = CGFloat(tileMap.numberOfRows) / 2.0 * tileSize.height
let rows = tileMap.numberOfRows
let columns = tileMap.numberOfColumns
for column in 0..<columns {
for row in 0..<rows {
let x = CGFloat(column) * tileSize.width - halfWidth + (tileSize.width / 2)
let y = CGFloat(row) * tileSize.height - halfHeight + (tileSize.height / 2)
if let tileDefinition = tileMap.tileDefinition(atColumn: column, row: row) {
if let name = tileDefinition.name {
let normalTexture = textureAtlas.textureNamed("\(name)_n")
tileDefinition.normalTextures = [normalTexture]
}
if (tileDefinition.userData?["shouldKill"] as? Bool ?? false) {
let newNode = SKShapeNode(rectOf: tileDefinition.size)
newNode.position = CGPoint(x: x, y: y)
newNode.isHidden = true
newNode.physicsBody = SKPhysicsBody(texture: tileDefinition.textures[0], size: tileDefinition.size)
newNode.physicsBody?.isDynamic = false
newNode.physicsBody?.affectedByGravity = false
newNode.physicsBody?.categoryBitMask = CollisionTypes.wall.rawValue
newNode.physicsBody?.collisionBitMask = CollisionTypes.dynamicComponents.rawValue
newNode.physicsBody?.contactTestBitMask = CollisionTypes.dynamicComponents.rawValue
self.addChild(newNode)
newNode.position = CGPoint(x: newNode.position.x + startingLocation.x, y: newNode.position.y + startingLocation.y)
}
}
}
}
}
The result for simulator--which is expected:
The result on device--which is incorrect:
I tried multiple simulators, it worked on them all. I've also tried multiple physical devices and it was broken on all of them.
The only thing that I could find while debugging is that the normal images on device seemed to be off by one pixel in size occasionally. So the normal size is 128 x 128 and occasionally the size on device would be 128 x 127 or 127 x 127. No clue what could cause this, nor if that is the actual problem.
Does anyone have any ideas as to why the normal maps would be rendered properly in the simulator, but not on device?
I am using on device image recognition from Catchoom CraftAR and working with the example available on Github https://github.com/Catchoom/craftar-example-ios-on-device-image-recognition.
The image recognition works, I would like to use the matchBoundingBox to draw some squares on all the 4 corners. Somehow the calculations I am doing are not working, I have based them on this article:
http://support.catchoom.com/customer/portal/articles/1886553-obtain-the-bounding-boxes-of-the-results-of-image-recognition
The square views are added to the scanning overlay and this is how I am calculating the points where to add the 4 views:
CraftARSearchResult *bestResult = [results objectAtIndex:0];
BoundingBox *box = bestResult.matchBoundingBox;
float w = self._preview.frame.size.width;
float h = self._preview.frame.size.height;
CGPoint tr = CGPointMake(w * box.topRightX , h * box.topRightY);
CGPoint tl = CGPointMake(w * box.topLeftX, h * box.topLeftY);
CGPoint br = CGPointMake(w * box.bottomRightX, h * box.bottomRightY);
CGPoint bl = CGPointMake(w * box.bottomLeftX, h * box.bottomLeftY);
The x position looks like it is pretty close, but the y position is completely off and looks like mirrored.
I am testing on iOS 10 iPhone 6s
Am I missing something?
The issue was that I was using the preview frame to make the translation to the points in screen. But the points that come through with bounding box are not relative to the preview view, they are relative to the VideoFrame (as the support people of catchoom.com pointed out). The VideoFrame size is set by the capturePreset which only accepts two values AVCaptureSessionPreset1280x720 and AVCaptureSessionPreset640x480. The default one is AVCaptureSessionPreset1280x720
So in my case I had to make the calculations with size 1280x720 and then make the conversion from those coordinates to the coordinates in my preview view size.
So it ended up looking like this:
let box = bestResult.matchBoundingBox
let wVideoFrame:CGFloat = 1080.0;
let hVideoFrame:CGFloat = 720.0;
let wRelativePreview = wVideoFrame/CGFloat(preview.frame.size.height)
let hRelativePreview = wVideoFrame/CGFloat(preview.frame.size.width)
var tl = CGPoint(x: wVideoFrame * CGFloat(box.topLeftX),y: hVideoFrame * CGFloat(box.topLeftY));
var tr = CGPoint(x: wVideoFrame * CGFloat(box.topRightX) ,y: hVideoFrame * CGFloat(box.topRightY));
var br = CGPoint(x: wVideoFrame * CGFloat(box.bottomRightX),y: hVideoFrame * CGFloat(box.bottomRightY));
var bl = CGPoint(x: wVideoFrame * CGFloat(box.bottomLeftX),y: hVideoFrame * CGFloat(box.bottomLeftY));
tl = CGPoint(x: tl.x/wRelativePreview, y: tl.y/hRelativePreview)
tr = CGPoint(x: tr.x/wRelativePreview, y: tr.y/hRelativePreview)
br = CGPoint(x: br.x/wRelativePreview, y: br.y/hRelativePreview)
bl = CGPoint(x: bl.x/wRelativePreview, y: bl.y/hRelativePreview)
// 4 square visualize top-left, top.right, bottom-left and bottom-right points
var fr = vTL.frame;
fr.origin = tl;
vTL.frame = fr;
fr.origin = tr;
vTR.frame = fr;
fr.origin = br;
vBR.frame = fr;
fr.origin = bl;
vBL.frame = fr;
Now the points looked quite ok on screen, but they looked some how rotated. So I rotated the view 90 degrees:
// overlay is the container of the 3 squares to visualize the points in screen
overlay.transform = CGAffineTransform(rotationAngle: CGFloat(M_PI/2.0))
Note this is not the official response from support from catchoom, this might not be 100% correct, but it worked for me quite well.
At the beginning I have declared the nodes.
//Declaration of the ground nodes.
var groundImage: SKSpriteNode = SKSpriteNode()
var groundImage2: SKSpriteNode = SKSpriteNode()
In the viewDidLoad I have:
//Creates an instance of both sprites that are of the same image that are to be lined up against each other in the x axis creating the illution of continuity.
groundImage = SKSpriteNode(imageNamed: "Ground.png")
groundImage2 = SKSpriteNode(imageNamed: "Ground.png")
//Specifies the Z position of the images.
groundImage.zPosition = 3
groundImage2.zPosition = 3
//Scales the images to the correct size of the screen.
groundImage.size.width = self.frame.width
groundImage.size.height = self.groundImage.size.height / 2
groundImage2.size.width = self.frame.width
groundImage2.size.height = self.groundImage2.size.height / 2
//Specicies the x position of the images. By offsetting the second you create the illution of a long, continuous image.
groundImage.position.x = view.bounds.size.width * 0.5
groundImage2.position.x = view.bounds.size.width * 1.5
//Specifies the y postion of the images, obviously these are the same as they are not to be offset at any time.
groundImage.position.y = (view.bounds.size.height - view.bounds.size.height) + self.groundImage.size.height / 2
groundImage2.position.y = (view.bounds.size.height - view.bounds.size.height) + self.groundImage2.size.height / 2
//Not sure what this does yet.
groundImage.texture?.filteringMode = SKTextureFilteringMode.Nearest
groundImage2.texture?.filteringMode = SKTextureFilteringMode.Nearest
//Adds instances of the sprites to the scene.
self.addChild(groundImage)
self.addChild(groundImage2)
In the update method I have:
//This is how the image is moved relative the number specified. The number in the variable is how many pixels the frame is being moved each frame refresh.
groundImage.position.x -= gameSpeed
groundImage2.position.x -= gameSpeed
if (groundImage.position.x <= -self.view!.bounds.size.width / 2)
{
groundImage.position.x = self.view!.bounds.size.width * 1.5 // - 2
}
if (groundImage2.position.x <= -self.view!.bounds.size.width / 2)
{
groundImage2.position.x = self.view!.bounds.size.width * 1.5 // - 2
}
Any yet there is a slight gap between the two images when they are looping. This gap increases as I increase the speed they are looped at using a game speed variable.
Can anyone explain to me what I have done wrong please?
I have checked the images themselves are not causing the issue.
Thanks,
Steven
The gap is probably because the update method called about 60 times in a second (defaults is 60 fps if I am not wrong),
you should use SKAction to simply flip your images,
it will be much more efficient. Here is your starting point:
https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/AddingActionstoSprites/AddingActionstoSprites.html
I'm building my first 2D platform game and I'm looking to set some limits to objects that are generated every few seconds using arc4Random.
Currently a bird will fly across the screen from right to left and most of the time the bird appears to be in the air, however sometimes the bird it at ground level that just looks strange.
What I would like to do is to set a minimum and maximum height the birds will be generated in, is this possible?
Here is some of the code...
func spawnBird() {
var birdP = SKNode()
birdP.position = CGPointMake( self.frame.size.width + birdTexture1.size().width * 2, 0 );
birdP.zPosition = -10;
var height = UInt32( self.frame.size.height / 1 )
var y = arc4random() % height;
var bird1 = SKSpriteNode(texture: birdTexture1)
//Code Removed
birds.addChild(birdP)
You could set a minimum and maximum height:
var height_max = UInt32( self.frame.size.height )
var height_min = UInt32( 20 )
var bird_range = arc4random_uniform(height_max - height_min + 1) + height_min;
Alternate method:
var bird_range = (arc4random() % (height_max - height_min) + 1) + height_min;
Methods Graphed:
The two using max/min height never got below 20, the original method you're using often hit 0.
this is standard problem.
int randomInIntRange(int minVal, int maxVal) {
return minVal+arc4random_uniform(maxVal-minVal+1);
}
CGFloat randomInFloatRange(CGFloat minVal, CGFloat maxVal) {
return minVal+(maxVal-minVal)*(arc4random()/(double)(UINT32_MAX));
}
I have 7 movieclips on stage I want to tween around an ellipse from different start points. I am having lots of trouble doing this.... I used a circle formula at first and then divided the y value by the width of the ellipse over the height. This sort of worked but after every rotation the y value was a little of. That code is:
this._x += (Math.cos(angle * Math.PI/180) * radius);
this._y += (Math.sin(angle * Math.PI/180) *radius)/1.54;
I also have trouble finding the angle of the start point, if it is off they won't travel in the same ellipse but they all have different starting angles.
Any clues?
Calculate the incidvidual offsets using this snippet:
// assuming you have your buttons in an array called buttons
for (var i:Number = 0; i < buttons.length; i++){
buttons[i].angleOffset = 360 / buttons.length * i;
}
Set the position each update instead of moving, that way you wont get any drift.
Update each object using this code, incrementing the angle var to get it to spin.
this._x = offsetX + Math.sin((angle + angleOffset) * Math.PI/180) * radius;
this._y = offsetY + Math.cos((angle + angleOffset) * Math.PI/180) * radius / 1.54;
This is almost soved, this piece of script will take the items of the array buttons (can add as many as you want), space them around the ellipse you set (origin + radius), and tween them around it according to the speed you set. The only problem is the spacing isn't even and some are close and some far apart and I don't understand why.
var angle:Number = 0;
var originX:Number = 200;
var originY:Number = 200;
var radiusX:Number = 267.5;
var radiusY:Number = 100;
var steps:Number = 360;
var speed:Number = 3.1415/steps;
var buttons:Array = new Array(this.age,this.ethnicity,this.sex,this.social,this.ability,this.orientation,this.faith);
for (i=0;i<buttons.length;i++) {
buttons[i].onEnterFrame = function() {
moveButtons(this);
controllButtons(this);
};
buttons[i]._order = (360/buttons.length) * (i+1);
}
function moveButtons(e) {
e._anglePhase = angle+e._order;
e._x = originX+Math.sin(e._anglePhase)*radiusX;
e._y = originY+Math.cos(e._anglePhase)*radiusY;
}
function controllButtons(e) {
angle += speed;
if (angle>=360) {
angle -= 360;
}
}
Please note I got the base of this script from http://www.actionscript.org/forums/showthread.php3?t=161830&page=2 converted it to AS2 and made it work from an array.