I am participating in the individual competition of the Nuggets Community Game Creativity Contest. For details, please see: Game Creativity Contest
foreword
I don't know if you have encountered this pinball game on the street. When you try it out, you can play it as soon as you play it. You can't play it as long as you pay for it. I remember that the first time I came into contact with this game, I was in junior high school. I saw this game on the street. My friends and I were deeply attracted by this game, so all our belongings were included. Happy to go home. At that time, the Internet was not yet developed, and I didn't know that this thing still had a mechanism. After understanding the principle , I feel that I am xxx. Next, we will use it SpriteKit
to implement a pinball game without routines
Install
1. Use and install TestFlight
the same as the previous Airplane Wars game installation
2. You can run the code to install, the demo address is here
game introduction
Pull the spring rod, so that the ball is subjected to different elastic forces to make it enter the grid. The gameplay is very simple, I won't say much, let's implement it below.
accomplish
1. The range (boundary) that the ball can move
We can see that the range of activity of the ball is the red area, so how can we make the ball only in the red area? In fact, the SpriteKit
system provides us with boundary-type physical bodies, including the following
// 在两点之间创建边
public /*not inherited*/ init(edgeFrom p1: CGPoint, to p2: CGPoint)
// 从路径创建边链
public /*not inherited*/ init(edgeChainFrom path: CGPath)
// 从路径创建循环边
public /*not inherited*/ init(edgeLoopFrom path: CGPath)
// 从矩形创建循环边
public /*not inherited*/ init(edgeLoopFrom rect: CGRect)
复制代码
We use the initialization method of creating edge loops from paths to physicsBody
create a boundary for the current scene, here we use Bezier curves to draw the path
private func setupPhysicsBody() {
let path = UIBezierPath()
path.lineWidth = 2
path.move(to: CGPoint(x: 0, y: size.height-size.width-topOffset))
path.addLine(to: CGPoint(x: 0, y: size.height-size.width/2-topOffset))
path.addArc(withCenter: CGPoint(x: size.width/2, y: size.height-size.width/2-topOffset), radius: size.width/2, startAngle: -.pi, endAngle: 0, clockwise: false)
path.addLine(to: CGPoint(x: size.width, y: size.height-size.width/2-topOffset))
path.addLine(to: CGPoint(x: size.width, y: size.height-size.width-topOffset))
path.close()
physicsBody = SKPhysicsBody(edgeLoopFrom: path.cgPath)
}
复制代码
2. Pull the spring to make the ball pop out
2.1. First, we touchesBegan
judge whether the touch is a spring in the method, if so, record the starting point of the touch, and set the gravitational acceleration of the current physical world to (0,0)
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
isTouch = false
offsetY = 0
physicsWorld.gravity = .zero
guard let touch = (touches as NSSet).anyObject() as? UITouch else { return }
let point = touch.location(in: self)
let node = atPoint(point)
switch node.name {
case "back":
view?.presentScene(MenuScene(size: size), transition: .fade(withDuration: 0.5))
case "tanhuang" where reward >= 50:
isTouch = true
startTouchPoint = point
default:
break
}
}
复制代码
2.2. touchesMoved
Calculate the offset of the finger movement in the method
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = (touches as NSSet).anyObject() as? UITouch else { return }
let point = touch.location(in: self)
if isTouch {
offsetY = point.y-startTouchPoint.y
if offsetY >= -80, offsetY <= 0 {
springNode.size = CGSize(width: 28, height: 120+offsetY)
ballNode.position = CGPoint(x: gridWidth * 8.5+3.5, y: size.height-size.width+74-topOffset+offsetY/2)
}
}
}
复制代码
touchesEnded
2.3. Calculate the required pulling force of the ball according to the offset in the method of releasing the spring , and set a gravitational acceleration for the current physical world. The default is (0,-9.8), here we change it to (0,- 3.5)
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if isTouch {
springNode.size = CGSize(width: 28, height: 120)
physicsWorld.gravity = CGVector(dx: 0, dy: -3.5)
let dy = 300 * -(offsetY/2) * 0.1
// 给小球添加一个向上的拉力
ballNode.physicsBody?.applyForce(CGVector(dx: 0, dy: dy))
offsetY = 0
view?.isUserInteractionEnabled = false
reward -= 50
}
}
复制代码
3. Detect the drop area of the ball
There are 9 areas where the ball finally falls, which are the area where the 8 ores are located and the area where the spring is located. Here we use the proxy didBegin
side to detect the drop area of the ball
extension PinballScene: SKPhysicsContactDelegate {
func didBegin(_ contact: SKPhysicsContact) {
// 弹簧所在的区域
let springArea = CGRect(x: CGFloat(8)*gridWidth, y: size.height-size.width-topOffset, width: gridWidth, height: 28)
if springArea.contains(ballN ode.position) {
view?.isUserInteractionEnabled = true
return
}
if let v = view, v.isUserInteractionEnabled {
return
}
for model in rewards {
if model.rewardArea.contains(ballNode.position) {
reward += model.reward
view?.isUserInteractionEnabled = true
}
}
}
}
复制代码
At this point, the realization of this game is over