I want to create one rectangle layer of progress bar around my object it maybe anything it should be an uibutton, uiview or any object so i create a below code:
let layer_ca = CAShapeLayer.init()
layer_ca.strokeColor = UIColor.green.cgColor
layer_ca.lineWidth = 10.0
layer_ca.fillColor = UIColor.clear.cgColor
let bezier_path = UIBezierPath.init(roundedRect: CGRect.init(x: captureButton.frame.origin.x, y: captureButton.frame.origin.y, width: captureButton.frame.width, height: captureButton.frame.height), cornerRadius: 15)
bezier_path.move(to: CGPoint(x: 12, y: 15))
layer_ca.path = bezier_path.cgPath
let animation = CABasicAnimation.init(keyPath: "strokeEnd")
animation.fromValue = NSNumber.init(value: 0.0)
animation.toValue = NSNumber.init(value: 1.0)
animation.duration = 2.0
layer_ca.add(animation, forKey: "myStroke")
self.view.layer.addSublayer(layer_ca)
here "captureButton" was an my object so its created successful above the object with the cabasicanimation and now what my question was how to change the ending point of the path and how should i stop the path to half of the distance path and then i continue with the same path which i stop previous path whenever i call to start.
i need exactly like as a rectangular progress bar around an object.
please help me anyone who should known the answer..!
I just have the same question, and spend a few hours to figure it out. if you want control the start point or end point, you should create your own BezierPath, and custom your path. bezierPath.move("start position"), and there it is, the last point will be the end position.
The code below is used to create a scene and create blocks in SceneKit. The blocks come out looking flat and not "3D enough" according to our users. Screenshots 1-2 show our app.
Screenshots 3-5 show what users expect the blocks to look like, that is more 3D-like.
After speaking to different people, there are different opinions about how to render blocks that look more like screenshots 3-5. Some people say use ambient occlusion, others say voxel lighting, some say use spot lighting and use shadows, or directional lighting.
We previously tried adding omni lighting, but that didn't work so it was removed. As you can see in the code, we also experimented with an ambient light node but that also didn't yield the right results.
What is the best way to render our blocks and achieve a comparable look to screenshots 3-5?
Note: we understand the code is not optimized for performance, i.e., that polygons are shown that should not be shown. That is okay. The focus is not on performance but rather on achieving more 3D-like rendering. You can assume some hard limit on nodes, like no more than 1K or 10K in a scene.
Code:
func createScene() {
// Set scene view
let scene = SCNScene()
sceneView.jitteringEnabled = true
sceneView.scene = scene
// Add camera node
sceneView.pointOfView = cameraNode
// Make delegate to capture screenshots
sceneView.delegate = self
// Set ambient lighting
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light!.type = SCNLightTypeAmbient
ambientLightNode.light!.color = UIColor(white: 0.50, alpha: 1.0)
//scene.rootNode.addChildNode(ambientLightNode)
//sceneView.autoenablesDefaultLighting = true
// Set floor
setFloor()
// Set sky
setSky()
// Set initial position for user node
userNode.position = SCNVector3(x: 0, y: Float(CameraMinY), z: Float(CameraZoom))
// Add user node
scene.rootNode.addChildNode(userNode)
// Add camera to user node
// zNear fixes white triangle bug while zFar fixes white line bug
cameraNode.camera = SCNCamera()
cameraNode.camera!.zNear = Double(0.1)
cameraNode.camera!.zFar = Double(Int.max)
cameraNode.position = SCNVector3(x: 0, y: 0, z: 0) //EB: Add some offset to represent the head
userNode.addChildNode(cameraNode)
}
private func setFloor() {
// Create floor geometry
let floorImage = UIImage(named: "FloorBG")!
let floor = SCNFloor()
floor.reflectionFalloffEnd = 0
floor.reflectivity = 0
floor.firstMaterial!.diffuse.contents = floorImage
floor.firstMaterial!.diffuse.contentsTransform = SCNMatrix4MakeScale(Float(floorImage.size.width)/2, Float(floorImage.size.height)/2, 1)
floor.firstMaterial!.locksAmbientWithDiffuse = true
floor.firstMaterial!.diffuse.wrapS = .Repeat
floor.firstMaterial!.diffuse.wrapT = .Repeat
floor.firstMaterial!.diffuse.mipFilter = .Linear
// Set node & physics
// -- Must set y-position to 0.5 so blocks are flush with floor
floorLayer = SCNNode(geometry: floor)
floorLayer.position.y = -0.5
let floorShape = SCNPhysicsShape(geometry: floor, options: nil)
let floorBody = SCNPhysicsBody(type: .Static, shape: floorShape)
floorLayer.physicsBody = floorBody
floorLayer.physicsBody!.restitution = 1.0
// Add to scene
sceneView.scene!.rootNode.addChildNode(floorLayer)
}
private func setSky() {
// Create sky geometry
let sky = SCNFloor()
sky.reflectionFalloffEnd = 0
sky.reflectivity = 0
sky.firstMaterial!.diffuse.contents = SkyColor
sky.firstMaterial!.doubleSided = true
sky.firstMaterial!.locksAmbientWithDiffuse = true
sky.firstMaterial!.diffuse.wrapS = .Repeat
sky.firstMaterial!.diffuse.wrapT = .Repeat
sky.firstMaterial!.diffuse.mipFilter = .Linear
sky.firstMaterial!.diffuse.contentsTransform = SCNMatrix4MakeScale(Float(2), Float(2), 1);
// Set node & physics
skyLayer = SCNNode(geometry: sky)
let skyShape = SCNPhysicsShape(geometry: sky, options: nil)
let skyBody = SCNPhysicsBody(type: .Static, shape: skyShape)
skyLayer.physicsBody = skyBody
skyLayer.physicsBody!.restitution = 1.0
// Set position
skyLayer.position = SCNVector3(0, SkyPosY, 0)
// Set fog
/*sceneView.scene?.fogEndDistance = 60
sceneView.scene?.fogStartDistance = 50
sceneView.scene?.fogDensityExponent = 1.0
sceneView.scene?.fogColor = SkyColor */
// Add to scene
sceneView.scene!.rootNode.addChildNode(skyLayer)
}
func createBlock(position: SCNVector3, animated: Bool) {
...
// Create box geometry
let box = SCNBox(width: 1.0, height: 1.0, length: 1.0, chamferRadius: 0.0)
box.firstMaterial!.diffuse.contents = curStyle.getContents() // "curStyle.getContents()" either returns UIColor or UIImage
box.firstMaterial!.specular.contents = UIColor.whiteColor()
// Add new block
let newBlock = SCNNode(geometry: box)
newBlock.position = position
blockLayer.addChildNode(newBlock)
}
Screenshots 1-2 (our app):
Screenshots 3-5 (ideal visual representation of blocks):
I still think there's a few easy things you can do that will make a big difference to how your scene is rendered. Apologies for not using your code, this example is something I had lying around.
Right now your scene is only lit by an ambient light.
let aLight = SCNLight()
aLight.type = SCNLightTypeAmbient
aLight.color = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1.0)
let aLightNode = SCNNode()
aLightNode.light = aLight
scene.rootNode.addChildNode(aLightNode)
If I use only this light in my scene I see the following. Note how all faces are lit the same irrespective of the direction they face. Some games do pull off this aesthetic very well.
The following block of code adds a directional light to this scene. The transformation applied in this light won't be valid for your scene, it's important to orientate the light according to where you want the light coming from.
let dLight = SCNLight()
dLight.type = SCNLightTypeDirectional
dLight.color = UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1.0)
let dLightNode = SCNNode()
dLightNode.light = dLight
var dLightTransform = SCNMatrix4Identity
dLightTransform = SCNMatrix4Rotate(dLightTransform, -90 * Float(M_PI)/180, 1, 0, 0)
dLightTransform = SCNMatrix4Rotate(dLightTransform, 37 * Float(M_PI)/180, 0, 0, 1)
dLightTransform = SCNMatrix4Rotate(dLightTransform, -20 * Float(M_PI)/180, 0, 1, 0)
dLightNode.transform = dLightTransform
scene.rootNode.addChildNode(dLightNode)
Now we have shading on each of the faces based on their angle relative to the direction of the light.
Currently SceneKit only supports shadows if you're using the SCNLightTypeSpot. Using a spotlight means we need to both orientate (as per directional light) and position it. I use this as a replacement for the directional light.
let sLight = SCNLight()
sLight.castsShadow = true
sLight.type = SCNLightTypeSpot
sLight.zNear = 50
sLight.zFar = 120
sLight.spotInnerAngle = 60
sLight.spotOuterAngle = 90
let sLightNode = SCNNode()
sLightNode.light = sLight
var sLightTransform = SCNMatrix4Identity
sLightTransform = SCNMatrix4Rotate(sLightTransform, -90 * Float(M_PI)/180, 1, 0, 0)
sLightTransform = SCNMatrix4Rotate(sLightTransform, 65 * Float(M_PI)/180, 0, 0, 1)
sLightTransform = SCNMatrix4Rotate(sLightTransform, -20 * Float(M_PI)/180, 0, 1, 0)
sLightTransform = SCNMatrix4Translate(sLightTransform, -20, 50, -10)
sLightNode.transform = sLightTransform
scene.rootNode.addChildNode(sLightNode)
In the above code we first tell the spotlight to cast a shadow, by default all nodes in your scene will then cast a shadow (this can be changed). The zNear and zFar settings are also important and must be specified so that the nodes casting shadows are within this range of distance from the light source. Nodes outside this range will not cast a shadow.
After shading/shadows there's a number of other effects you can apply easily. Depth of field effects are available for the camera. Fog is similarly easy to include.
scene.fogColor = UIColor.blackColor()
scene.fogStartDistance = 10
scene.fogEndDistance = 110
scenekitView.backgroundColor = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1.0)
Update
Turns out you can get shadows from a directional light. Modifying the spotlight code from above by changing its type and setting the orthographicScale. Default value for orthographicScale seems to be 1.0, obviously not suitable for scenes much larger than 1.
let dLight = SCNLight()
dLight.castsShadow = true
dLight.type = SCNLightTypeDirectional
dLight.zNear = 50
dLight.zFar = 120
dLight.orthographicScale = 30
let dLightNode = SCNNode()
dLightNode.light = dLight
var dLightTransform = SCNMatrix4Identity
dLightTransform = SCNMatrix4Rotate(dLightTransform, -90 * Float(M_PI)/180, 1, 0, 0)
dLightTransform = SCNMatrix4Rotate(dLightTransform, 65 * Float(M_PI)/180, 0, 0, 1)
dLightTransform = SCNMatrix4Rotate(dLightTransform, -20 * Float(M_PI)/180, 0, 1, 0)
dLightTransform = SCNMatrix4Translate(dLightTransform, -20, 50, -10)
dLightNode.transform = dLightTransform
scene.rootNode.addChildNode(dLightNode)
Produces the following image.
The scene size is 60x60, so in this case setting the orthographic scale to 30 produces shadows for the objects close to the light. The directional light shadows appear different to the spot light due to the difference in projections (orthographic vs perspective) used when rendering the shadow map.
Ambient occlusion calculations will give you the best results, but is very expensive, particularly in a dynamically changing world, which it looks like this is.
There are several ways to cheat, and get the look of Ambient occlusion.
Here's one:
place transparent, gradient shadow textures on geometry "placards" used to place/present the shadows at the places required. This will involve doing checks of geometry around the new block before determining what placards to place, with which desired texture for the shadowing. But this can be made to look VERY good, at a very low cost in terms of polygons, draw calls and filtrate. It's probably the cheapest way to do this, and have it look good/great, and can only really be done (with a good look) in a world of blocks. A more organic world rules this technique out. Please excuse the pun.
Or, another, similar: Place additional textures onto/into objects that have the shadow, and blend this with the other textures/materials in the object. This will be a bit fiddly, and I'm not an expert on the powers of materials in Scene Kit, so can't say for sure this is possible and/or easy in it.
Or: Use a blend of textures with a vertex shader that's adding a shadow from the edges that touch or otherwise need/desire a shadow based on your ascertaining what and where you want shadows and to what extent. Will still need the placards trick on the floors/walls unless you add more vertices inside flat surfaces for the purpose of vertex shading for shadows.
Here's something I did for a friend's CD cover... shows the power of shadows. It's orthographic, not true 3D perspective, but the shadows give the impression of depths and create the illusions of space:
all answers above (or below) seem to be good ones (at the time of this writing) however,
what I use (just for setting up a simple scene) is one ambient light (lights everything in all directions) to make things visible.And then one omnidirectional light positioned somewhere in the middle of your scene, the omni light can be raised up (Y up I mean) to light the whole of your scene. The omni light gives the user a sense of shading and the ambient light makes it more like a sun light.
for example:
Imagine sitting in a living room (like I am right now) and the sun-light peers through the window to your right.
You can obviously see a shadow of an area that the couch is not getting sun light, however you can still see details of what is in the shadow.
Now! all the sudden your wold gets rid of ambient light BOOM! The shadow is now pitch black, you can't anymore see what is in the shadow.
Or say the ambient light came back again (what a relief), but all the sudden the omni light stopped working. (probably my fault :( ) Everything now is lighted the same, no shadow, no difference, but if you lay a paper on the table, and look at it from above, there is no shadow! So you think it is part of the table! In a world like this your rely on the contour of something in order to see it- you would have to look at the table from side view, to see the thickness of the paper.
Hope this helps (at least a little)
Note: ambient lighting give a similar effect to emissive material
I want to create a ring with a 3D effect using Sprite Kit. (SEE IMAGES)
I tried subclassing a SKNode and adding two nodes as children. (SEE CODE)
One node was a complete SKShapeNode ellipse, and the other was half ellipse using SKCropNode with a higher zPosition.
It looks good, but the SKCropNode increases the app CPU usage from 40% to 99%.
Any ideas on how to reduce the SKCropNode performance cost, or any alternative to create the same ring 3D effect?
class RingNode: SKNode {
let size: CGSize
init(size: CGSize, color: SKColor)
{
self.size = size
self.color = color
super.init()
ringPartsSetup()
}
private func ringPartsSetup() {
// LEFT PART (half ellipse)
let ellipseNodeLeft = getEllipseNode()
let leftMask = SKSpriteNode(texture: nil, color: SKColor.blackColor(), size: CGSize(
width: ellipseNodeLeft.frame.size.width/2,
height: ellipseNodeLeft.frame.size.height))
leftMask.anchorPoint = CGPoint(x: 0, y: 0.5)
leftMask.position = CGPoint(x: -ellipseNodeLeft.frame.size.width/2, y: 0)
let leftNode = SKCropNode()
leftNode.addChild(ellipseNodeLeft)
leftNode.maskNode = leftMask
leftNode.zPosition = 10 // Higher zPosition for 3D effect
leftNode.position = CGPoint(x: -leftNode.frame.size.width/4, y: 0)
addChild(leftNode)
// RIGHT PART (complete ellipse)
let rightNode = getEllipseNode()
rightNode.position = CGPoint(x: 0, y: 0)
rightNode.zPosition = 5
addChild(rightNode)
}
private func getEllipseNode() -> SKShapeNode {
let ellipseNode = SKShapeNode(ellipseOfSize: CGSize(
width: size.width,
height: size.height))
ellipseNode.strokeColor = SKColor.blackColor()
ellipseNode.lineWidth = 5
return ellipseNode
}
}
You've got the right idea with your two-layer approach and the half-slips on top. But instead of using a shape node inside a crop node, why not just use a shape node whose path is a half-ellipse? Create one using either CGPath or UIBezierPath API — use a circular arc with a transform to make it elliptical — then create your SKShapeNode from that path.
You may try converting your SKShapeNode to an SKSpriteNode. You can use SKView textureFromNode: (but we aware of issues with scale that require you to use it only after the node has been added to the view and at least one update cycle has run), or from scratch using an image (created programatically with a CGBitmapContext, of course).
This should be really straightforward. I'm trying to have my particles fade out using a keyframe sequence.. but when I use the keyframe sequence they dont fade out at all. Not sure what I could be doing wrong.
particle creation:
static func debris(size: Int) -> Array<SKEmitterNode> {
if size > 5 {
fatalError("we don't have that much debris")
}
var debrisArr: [SKEmitterNode] = []
for i in 1...size {
let debris: SKEmitterNode = SKEmitterNode(fileNamed: "debris")
debris.particleTexture = SKTexture(imageNamed: "debris\(i)")
convertNumRef(&debris.particleScale)
convertNumRef(&debris.particleScaleRange)
debris.particleRotationSpeed = debris.particleRotationSpeed * CGFloat.randomSign()
// THE PART WE CARE ABOUT
debris.particleAlphaSequence = SKKeyframeSequence(keyframeValues: [0.5, 1.0, 0.0], times: [0.0, 3.0, 4.0])
debrisArr.append(debris)
}
add particles to game scene here
func makeDebris(){
for debrisEmitter in self.debris {
debrisEmitter.resetSimulation()
debrisEmitter.position = self.position
self.gameScene.gameLayer.addChild(debrisEmitter)
debrisEmitter.runAction(SKAction.removeFromParentAfterDelay(10))
}
}
I've tried this using a simpler example too.
fire is the default spritekit "fire" particle
let fire = SKEmitterNode(fileNamed: "MyParticle")
fire.particleColorSequence = SKKeyframeSequence(keyframeValues: [SKColor.blueColor(), SKColor.blueColor(), SKColor.yellowColor()], times: [0.0, 1.0, 2.0])
fire.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
self.addChild(fire)
the emitter is only emitting blue particles. it just picks whichever color is first in the array. I must be missing something.
Ok I misunderstood. the times arent the times in seconds. they are fractions of your particle's lifespan
debris.particleLifetime = 8
debris.particleAlphaSequence = SKKeyframeSequence(keyframeValues: [1.0, 1.0, 0.0], times: [0.0, 0.7, 1.0])
How can I draw such a conical gradient in iOS using Core Graphics / Quartz 2D API?
(source: ods.com.ua)
If anyone is still looking for a solution, Apple finally introduced .conic gradient type in iOS 12. Perfect for masking to create circular progress bar with gradient.
Example:
let gradientLayer = CAGradientLayer()
gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0.5, y: 0)
gradientLayer.type = .conic
gradientLayer.colors = [UIColor.red.cgColor, UIColor.orange.cgColor, UIColor.green.cgColor]
gradientLayer.frame = bounds
Recently I've made a custom CALayer class for this: AngleGradientLayer
It's not tested for performance, so beware.
I wanted pure Swift solution for this, and it was also kind of a challenging task for me.
At the end, I wrote AEConicalGradient which does that with interesting approach (by using a circle and drawing lines of different colors to the center).
There is no Quartz function for this style of gradient. Unless you're ready to dig into mathematics behind it, I'd suggest you use pre-made images for this. It's not a problem if you need it only for opacity mask.