Swift4游戏适配iPad/iPhoneX时.aspectFill倾斜及碰撞坐标修正问题
Hey there! Let's work through these two issues you're hitting in your Swift 4 game—screen distortion on iPad/iPhone X and collision position correction. I've got step-by-step solutions for both:
The tilt you're seeing is likely due to a mismatch between your fixed 750x1334 scene size and the varying aspect ratios of different devices (iPad is 4:3, iPhone X is 19.5:9, while your scene is 9:16). Here’s how to fix it:
Ensure Proper Scene Initialization: When loading your
GameScene.sks, make sure yourSKViewmatches the device’s screen bounds and you handle aspect ratio correctly:if let view = self.view as? SKView { guard let scene = SKScene(fileNamed: "GameScene") else { return } scene.scaleMode = .aspectFill view.frame = self.view.bounds view.ignoresSiblingOrder = true view.presentScene(scene) }Adjust Scene Size for Device Aspect Ratio: Add this code to your
GameScene’sdidMove(to:)method to dynamically adjust the scene size while preserving your original aspect ratio:override func didMove(to view: SKView) { let viewRatio = view.bounds.width / view.bounds.height let sceneRatio = size.width / size.height if viewRatio > sceneRatio { // View is wider than scene—adjust height to match view width size = CGSize(width: view.bounds.width, height: view.bounds.width / sceneRatio) } else { // View is taller than scene—adjust width to match view height size = CGSize(width: view.bounds.height * sceneRatio, height: view.bounds.height) } }Lock Interface Orientation: Double-check your
Info.plist’sSupported interface orientationsto ensure you’re only supporting the orientation your game is designed for (e.g., portrait). This prevents unexpected scene rotation that can cause tilt.
To fix the overlapping issue and make your sprite snap to the boulder based on its direction of approach, use the physics contact’s normal and penetration depth to calculate the correct position. Here’s how:
First, make sure your physics bodies are properly configured (set categoryBitMask, contactTestBitMask, and ensure the boulder is isDynamic = false if it’s stationary). Then update your didBegin(_ contact:) method:
func didBegin(_ contact: SKPhysicsContact) { // Identify which bodies are the player and dirt boulder var player: SKSpriteNode? var boulder: SKSpriteNode? if (contact.bodyA.node?.name == "player" && contact.bodyB.node?.name == "dirt boulder") { player = contact.bodyA.node as? SKSpriteNode boulder = contact.bodyB.node as? SKSpriteNode } else if (contact.bodyB.node?.name == "player" && contact.bodyA.node?.name == "dirt boulder") { player = contact.bodyB.node as? SKSpriteNode boulder = contact.bodyA.node as? SKSpriteNode } guard let playerSprite = player, let boulderSprite = boulder else { return } // Get collision data: normal (direction of collision) and penetration depth (overlap distance) let contactNormal = contact.contactNormal let penetration = contact.penetrationDepth // Calculate correction vector to move player out of overlap // Reverse the normal because it points from the boulder to the player let correction = CGVector(dx: contactNormal.dx * penetration, dy: contactNormal.dy * penetration) playerSprite.position = CGPoint( x: playerSprite.position.x - correction.dx, y: playerSprite.position.y - correction.dy ) // Stop player movement immediately playerSprite.physicsBody?.velocity = .zero }
Why this works:
contactNormal: A vector pointing from the boulder to the player, telling you exactly which direction the collision came from.penetrationDepth: The distance the player has overlapped with the boulder—we use this to move the player just far enough to eliminate the overlap.
This method is far more accurate than hardcoding position adjustments because it uses the physics engine’s precise collision data.
内容的提问来源于stack exchange,提问作者user3301173

