Reputation: 9
How do I vertically align several SKSpriteNodes
and keep them aligned when they move?
EDITED 1/4/2025
After initial startup of game:
After start of motion:
Note that after moving, the locomotive is OK because it is following the UIBezierPath
as it should. Ditto for the 3 train cars.
The PROBLEM is the lack of vertical alignment of the locomotive with the trailing train cars. The cars are positioned okay, but the locomotive is elevated??
My strong hunch is that this PROBLEM rests with my train’s animated code which I will present further down.
My code to start movement along the UIBezierPath
(myTrack
) is as follows:
func startFollowTrainPath() {
var trainAction = SKAction.follow(trainPath.cgPath,
asOffset: false,
orientToPath: true,
speed: thisSpeed)
trainAction = SKAction.repeatForever(trainAction)
myTrain.run(trainAction)
myTrain.isPaused = false
}
Similar code exists for the cars, e.g., func startFollowCoalcarPath()
// called once by `SKScene’s setupScene(...)`
func getGameNodes() {
// courtesy of @DonMag
guard let img = UIImage(named: "train.png"),
let imgTop = img.rotated(byDegrees: -90.0),
let imgBottom = imgTop.horizontallyMirrored() else {
fatalError("Could not load \"train.png\" image!")
}
let trainInit = SKTexture(image: img)
trainTop = SKTexture(image: imgTop)
trainBottom = SKTexture(image: imgBottom)
myTrain = SKSpriteNode(texture: trainInit)
myTrain.name = "train"
myTrain.zPosition = trainZPosition
myTrain.zRotation = trainZRotation
guard let img = UIImage(named: "coalcar.png"),
let imgTop = img.rotated(byDegrees: -90.0),
let imgBottom = imgTop.horizontallyMirrored() else {
fatalError("Could not load \"coalcar.png\" image!")
}
let coalcarInit = SKTexture(image: img)
coalcarTop = SKTexture(image: imgTop)
coalcarBottom = SKTexture(image: imgBottom)
myCoalcar = SKSpriteNode(texture: coalcarInit)
myCoalcar.name = "coalcar"
myCoalcar.zPosition = coalcarZPosition
myCoalcar.zRotation = coalcarZRotation // = 0.0
myCoalcar.anchorPoint = CGPointMake(0.5, 1.0)
// etc. for freightcar + baggagecar
}
My code for initially vertically aligning all components =
override func sceneDidLoad() {
super.sceneDidLoad()
addGamePieces() // calls addChild for all SKSpriteNodes
sizeGameNodes()
positionGameNodes()
}
func sizeGameNodes() {
sizeTracks()
sizeTrainNodes()
}
func sizeTracks() {
itsGameViewController.sizeRoom()
tracksWidth = roomWidth - 2*trackOffset
tracksHeight = roomHeight/2 - trackOffset
myTracks.size = CGSizeMake(tracksWidth, tracksHeight)
}
func sizeTrainNodes() {
#if os(iOS)
if (roomWidth > roomHeight) { trainScale = 1.5 }
else { trainScale = 1.2 }
#elseif os(tvOS)
trainScale = 2.0
#endif
trainWidth = 96.0*trainScale // original size = 96 x 110
trainHeight = 110.0*trainScale
myTrain.size = CGSizeMake(trainWidth, trainHeight)
}
func positionGameNodes() {
positionTracks()
positionTrainNodes()
}
func positionTracks() {
tracksPosX = roomPosX // center horizontally
tracksPosY = roomPosY - tracksHeight/2
if myTracks.position == .zero {
myTracks.position = CGPointMake(tracksPosX, tracksPosY)
}
topOfTracks = 0.0 // = tracksPosY + tracksHeight/2
}
func positionTrainNodes() {
let theRect = CGRect(x: tracksPosX - tracksWidth/2,
y: tracksPosY - tracksHeight/2,
width: tracksWidth,
height: tracksHeight)
// line up train vertically with its cars
trainPosY = topOfTracks + trainHeight/2
trainPosX = tracksPosX + 130.0 // and a tad to the right of center
// when Swift creates an Object, its .position is initialized = .zero
if myTrain.position == .zero {
myTrain.position = CGPointMake(trainPosX, trainPosY)
}
// etc. for 3 train cars
}
Whoops ... here's my GameScene's update(...)
method:
override func update(_ currentTime: TimeInterval) {
super.update(currentTime)
let t = myTrain.position
if t.y < trainPath.bounds.midY && trainIsOnTopOfOval
{
trainIsOnTopOfOval = false
itsGameViewController.stopAnimatedTrain()
itsGameViewController.initAnimatedTrainHere("bottom")
itsGameViewController.startAnimatedTrain()
// set orientation of each car = that of myTrain
myCoalcar.texture = coalcarBottom
myFreightcar.texture = freightcarBottom
myBaggagecar.texture = baggagecarBottom
}
else if t.y > trainPath.bounds.midY && !trainIsOnTopOfOval
{
trainIsOnTopOfOval = true
itsGameViewController.stopAnimatedTrain()
itsGameViewController.initAnimatedTrainHere("top")
itsGameViewController.startAnimatedTrain()
// set orientation of each car = that of myTrain
myCoalcar.texture = coalcarTop
myFreightcar.texture = freightcarTop
myBaggagecar.texture = baggagecarTop
}
}
Finally, here is the animated code snippets which I guessed above were the cause of the stated problem:
I start movement of the animated train when I start a new game:
func newGame() {
initAnimatedTrainHere("top")
}
func initAnimatedTrainHere(_ toWhere: String) {
#if os(iOS)
let trainImgFolder = "images_iOS/Railroad/animatedTrainFolder/"
#elseif os(tvOS)
let trainImgFolder = "images_tvOS/Railroad/animatedTrainFolder/"
#endif
animatedTrainImgArray =
[trainImgFolder + "animatedTrain_0.png",
trainImgFolder + "animatedTrain_1.png",
trainImgFolder + "animatedTrain_2.png",
trainImgFolder + "animatedTrain_3.png",
// 4 .. 16
trainImgFolder + "animatedTrain_17.png",
trainImgFolder + "animatedTrain_18.png",
trainImgFolder + "animatedTrain_19.png"]
let trainArray = createImgArrayWithSrc(animatedTrainImgArray,
whereTo: toWhere)
// This just initializes the SKAction ...
// run with repeatForever below actually starts the animation.
animatedTrainAction = SKAction.animate(with: trainArray,
timePerFrame: 0.05)
}
When I want to start movement of the train + train cars, I call
func startTrain() {
myTrain.run(SKAction.repeatForever(animatedTrainAction),
withKey: animatedTrainKey)
}
Since this request for help has been fairly long, I repeat:
The initial positions of the train + cars move as they should when startTrain()
is called .. but
The PROBLEM is the lack of vertical alignment of the locomotive with the trailing train cars both in their initial start-up position and when they start moving. The cars are positioned okay, but the locomotive is elevated??
Upvotes: 0
Views: 20