.GetObjVisibility LDY currentObject \ Set Y to the number of the object to check (so we are \ checking object #Y) CPY playerObject \ If this is the player object, jump to objv6 to return BEQ objv6 \ from the subroutine with the C flag set to indicate \ that the object is not visible on-screen, as the \ player can never see themselves JSR GetObjectAngles \ Calculate the angles and distances of the vector from \ the viewer to object #Y and put them into the \ following variables: \ \ * Set objectViewYaw(Hi Lo) to the yaw angle of the \ viewer's gaze towards the object, relative to the \ screen \ \ * Set hypotenuse(Hi Lo) to the length of the 3D \ vector from the viewer to the object when \ projected down onto the ground plane \ We now work out the width of the object that we are \ looking at so we can work out whether any of it is \ on-screen LDY currentObject \ Set X to the type of the object that we are checking LDX objectTypes,Y LDA objectHalfWidth,X \ Set A to half the object width for this object type, \ as a fraction of a tile CMP minObjWidth \ If A < minObjWidth then set A = minObjWidth, so: BCS objv1 \ LDA minObjWidth \ A = max(minObjWidth, objectHalfWidth) \ \ So if minObjWidth is non-zero, we consider the object \ to be at least as wide as minObjWidth for the purposes \ of calculating whether it is visible on-screen .objv1 STA xDeltaLo \ Set (A xDeltaLo) = (0 A) LDA #0 \ = max(minObjWidth, objectHalfWidth) \ \ So xDeltaLo is the width of the object STA minObjWidth \ Set minObjWidth = 0 as we have used this figure in the \ visibility calculation for the object, so we need to \ reset it for the next time we check an object (so the \ default value for minObjWidth is zero and it is only \ overridden when a large object is replaced by a \ smaller object) JSR GetPitchAngleDelta \ At this point we construct a right-angled triangle \ with adjacent side hypotenuse(Hi Lo) and opposite \ side xDeltaLo, like this: \ \ object \ _.-+ \ _.-´ | \ _.-´ | \ _.-´ | \ _.-´ | xDeltaLo \ _.-´ | \ .-´ angle(Hi Lo) | \ viewer +--------------------------+ \ hypotenuse(Hi Lo) \ \ This triangle is flat on the ground with the viewer at \ the corner \ \ The adjacent side of length hypotenuse(Hi Lo) along \ the bottom of the triangle represents the projection \ onto the ground of the vector from the viewer to the \ object, as if there were a light shining down from \ above, so this is the projection of the gaze vector \ \ We want to calculate angle(Hi Lo), as that's the yaw \ angle delta between the projected gaze vector and the \ edge of the object, as the object has a yaw angle of \ xDeltaLo from the object centre to the side (as it's \ the half-width of the object) \ \ We can then use this yaw angle delta to work out \ whether the left and right edges of the object are \ within the left-right viewing arc of the screen, and \ therefore whether any part of the object is within the \ screen bounds \ \ To do this we call GetPitchAngleDelta, not because we \ are calculating a pitch angle, but because this \ routine calculates the angle of the above triangle as \ part of the process of calculating a pitch angle delta \ (so we just ignore the pitch angle result here) \ We now check whether the object is on screen in terms \ of yaw angles, so that's checking the object's edges \ against the left and right edges of the screen \ \ We start by checking the object against the left edge \ of the screen LDA objectViewYawLo \ Set (A T) = objectViewYaw(Hi Lo) - angle(Hi Lo) SEC \ SBC angleLo \ So (A T) contains the yaw angle of the left edge of STA T \ the object, relative to the screen LDA objectViewYawHi SBC angleHi BPL objv2 \ If the result in (A T) is positive then the left edge \ of the object is to the right of the left screen edge, \ so jump to objv2 to see how far it is to the right LDA #0 \ (A T) is negative, so the left edge of the object is \ to the left of the left screen edge, so set A = 0 to \ use as the yaw offset of the left edge of the object \ in objYawOffset BEQ objv3 \ Jump to objv3 to set objYawOffset to zero and move on \ to the checks for the right edge of the object .objv2 ASL T \ Double the yaw angle in (A T) ROL A \ \ This converts the yaw angle into character columns in \ screen memory \ \ The custom screen mode 5 used by the game is 40 \ character columns wide, and the screen is 20 yaw \ angles wide in terms of the game's coordinate system \ \ So we can simply double the yaw angle in (A T) to \ convert it into on-screen character columns CMP #40 \ If A >= 40 then the left edge is past the right edge BCS objv6 \ of the screen (as the screen is 40 columns wide), \ which means the object is not visible, so jump to \ objv6 to return from the subroutine with the C flag \ set to indicate that the object is not visible \ on-screen .objv3 STA objYawOffset \ Set objYawOffset to A, so it contains the yaw offset \ of the left edge of the object from the left edge of \ the screen, in character columns \ We now check the object against the right edge of the \ screen LDA objectViewYawLo \ Set (A T) = objectViewYaw(Hi Lo) + angle(Hi Lo) CLC \ ADC angleLo \ So (A T) contains the yaw angle of the right edge of STA T \ the object, relative to the screen LDA objectViewYawHi ADC angleHi BMI objv6 \ If the result in (A T) is negative then the right edge \ of the object is to the left of the left screen edge, \ so jump to objv6 to return from the subroutine with \ the C flag set to indicate that the object is not \ visible on-screen \ If we get here then the right edge of the object is to \ the right of the left screen edge, and we know from \ the first test that the left edge of the object is to \ the left of the right screen edge, so some of the \ object must be on-screen ASL T \ Double the yaw angle in (A T) ROL A \ \ This converts the yaw angle into character columns in \ screen memory \ \ The custom screen mode 5 used by the game is 40 \ character columns wide, and the screen is 20 yaw \ angles wide in terms of the game's coordinate system \ \ So we can simply double the yaw angle in (A T) to \ convert it into on-screen character columns CMP #40 \ If A >= 40 then set A = 39, so A = max(39, A) BCC objv4 \ LDA #39 \ This sets A to the yaw angle offset of the right edge \ of the object, in on-screen character columns, clipped \ to fit within the screen width of 40 columns .objv4 CLC \ Set objYawWidth = A + 1 - objYawOffset ADC #1 \ SEC \ This sets objYawWidth to the difference between the SBC objYawOffset \ yaw offsets for the right and left edges, so that's STA objYawWidth \ the width of the object in on-screen character columns \ \ We add 1 to ensure we take the fractional part of the \ yaw offsets into account, as we have ignored the low \ byte in T for this calculation BEQ objv6 \ If objYawWidth = 0 then the object has no on-screen \ width, so jump to objv6 to return from the subroutine \ with the C flag set to indicate that the object is not \ visible on-screen CMP #21 \ If A >= 21 then set A = 20, so A = max(20, A) BCC objv5 \ LDA #20 \ This clips A to the maximum of 20 character columns as \ this is the maximum width of the screen buffer when \ configured as a column buffer .objv5 STA bufferColumns \ Configure the screen buffer to use A character columns \ for drawing the object \ \ Note that this has a maximum value of 20, which is why \ close-up objects that take up a large amount of space \ on-screen are absorbed in two stages, left then right, \ as the object it too wide to fit into the column \ buffer's maximum width of 20 character columns, or \ half a screen CLC \ Clear the C flag to indicate that the object is \ visible on-screen RTS \ Return from the subroutine .objv6 SEC \ Set the C flag to indicate that the object is not \ visible on-screen RTS \ Return from the subroutineName: GetObjVisibility [Show more] Type: Subroutine Category: Gameplay Summary: Calculate whether any part of an object is visible on-screen, and if so, which character columns it spans on the screenContext: See this subroutine in context in the source code References: This subroutine is called as follows: * CheckObjVisibility calls GetObjVisibility * DrawUpdatedObject calls GetObjVisibility
Arguments: currentObject The number of the object to check
Returns: C flag The object's visibility: * Clear = at least some of the object is visible on-screen * Set = the object is not visible on-screen bufferColumns If the object is visible, this is set to the number of character columns in the screen buffer that the object spans objYawOffset If the object is visible, this is set to the yaw offset of the left edge of the object from the left edge of the screen, in character columns
[X]
Subroutine GetObjectAngles (category: 3D objects)
Calculate the angles and distances of the vector from the viewer to a specific object
[X]
Subroutine GetPitchAngleDelta (category: Maths (Geometry))
Calculate the pitch angle of a vector relative to an object's pitch angle
[X]
Variable bufferColumns in workspace Main variable workspace
The number of character columns in the current screen buffer
[X]
Variable currentObject in workspace Zero page
The number of the object we are currently processing
[X]
Variable minObjWidth in workspace Main variable workspace
The on-screen width to use when updating objects
[X]
Variable objYawOffset in workspace Main variable workspace
The yaw offset of the left edge of the object being analysed in GetObjVisibility, relative to the left edge of the screen, in on-screen character columns
[X]
Variable objYawWidth in workspace Main variable workspace
The width of the visible portion of the object being analysed in GetObjVisibility, in on-screen character columns
[X]
Variable objectHalfWidth (category: Gameplay)
Each object's width in terms of tile widths, for half the object
[X]
Variable objectTypes (category: 3D objects)
The object types table for up to 64 objects
[X]
Variable objectViewYawHi in workspace Main variable workspace
The yaw angle of the object being analysed, relative to the current viewer (high byte)
[X]
Variable objectViewYawLo in workspace Main variable workspace
The yaw angle of the object being analysed, relative to the current viewer (low byte)
[X]
Label objv1 is local to this routine
[X]
Label objv2 is local to this routine
[X]
Label objv3 is local to this routine
[X]
Label objv4 is local to this routine
[X]
Label objv5 is local to this routine
[X]
Label objv6 is local to this routine
[X]
Variable playerObject in workspace Zero page
The number of the player object