In my Blitzbasic program as listed below, I put everything in for a simple walk around and look with mouse FPS. However, when I tried to make a crosshair that followed the mouse, I was unsuccessful for some unknown reason.
AppTitle "Colby's FPS"
Graphics3D 800,600,64,2
SetBuffer BackBuffer()
SeedRnd MilliSecs()
;HidePointer
;OBJECTS
Global crosshairs = LoadImage("crosshairs.bmp")
Global camera = CreateCamera()
Global cube = CreateCube()
Global light = CreateLight()
;OBJECT SPECS
MaskImage crosshairs,0,0,0
MidHandle crosshairs
PositionEntity camera,50,0,50
PositionEntity cube,50,0,45
PositionEntity light,0,10,0
;CALLED FUNCTIONS
While Not KeyHit(1)
;Cls
;Crosshair Follow
DrawImage crosshairs,20,20
;Camera Rotate
mxs# = mxs# + MouseXSpeed()
If mxs# > 360 Then mxs# = 0
If mxs# < 0 Then mxs# = 360
RotateEntity camera,0,-mxs#,0
RenderWorld()
UpdateWorld()
cameraCtrl()
Flip
Wend
End
;FUNCTIONS
Function cameraCtrl()
If KeyDown(208)
MoveEntity camera,0,0,-0.2
ElseIf KeyDown(200)
MoveEntity camera,0,0,0.2
ElseIf KeyDown(203)
MoveEntity camera,-0.2,0,0
ElseIf KeyDown(205)
MoveEntity camera,0.2,0,0
EndIf
If KeyDown(31)
MoveEntity camera,0,0,-0.2
ElseIf KeyDown(17)
MoveEntity camera,0,0,0.2
ElseIf KeyDown(30)
MoveEntity camera,-0.2,0,0
ElseIf KeyDown(32)
MoveEntity camera,0.2,0,0
EndIf
I think you mean centering the mouse as you rotate.
In this case, put this line before RenderWorld():
...
MoveMouse GraphicsWidth() / 2, GraphicsHeight() / 2 <-----------
RenderWorld()
UpdateWorld()
...
Related
I am trying to complete this Lua tic-tac-toe tutorial in Corona SDK.
I managed to get through the 1st part but got lost during this 2nd part where he is establishing variables within a table to register the alternating turns for "x" and "o".
He is using the tap count to determine which turn it is and I did try to use Corona's touch.id to mimic this technique, to no avail.
I am hoping someone can explain how I can achieve this using Corona.
Here is what I have so far (from Part1 of Tutorial):
d = display
w20 = d.contentWidth * .2
h20 = d.contentHeight * .2
w40 = d.contentWidth * .4
h40 = d.contentHeight * .4
w60 = d.contentWidth * .6
h60 = d.contentHeight * .6
w80 = d.contentWidth * .8
h80 = d.contentHeight * .8
----DRAW LINES FOR BOARD
local lline = d.newLine(w40,h20,w40,h80 )
lline.strokeWidth = 5
local rline = d.newLine(w60,h20,w60,h80 )
rline.strokeWidth = 5
local bline = d.newLine(w20,h40,w80,h40 )
bline.strokeWidth = 5
local tline = d.newLine(w20,h60,w80,h60 )
tline.strokeWidth = 5
--PLACE BOARD COMPARTMENT DIMENSIONS IN TABLE
board ={
{"tl", 1, w20, h40, w40, h20,0},
{"tm",2, w40,h40,w60,h20,0},
{"tr",3, w60,h40,w80,h20,0},
{"ml", 4, w20, h60, w40, h40,0},
{"mm",5, w40,h60,w60,h40,0},
{"mr",6, w60,h60,w80,h40,0},
{"bl", 7, w20, h80, w40, h60,0},
{"bm",8, w40,h80,w60,h60,0},
{"br",9, w60,h80,w80,h60,0}
}
--
--FILL COMPARTMENT W/ COLOR WHEN TOUCHED
local function fill (event)
if event.phase == "began" then
tap = 0
for t = 1, 9 do
if event.x > board[t][3] and event.x < board [t][5] then
if event.y < board[t][4] and event.y > board[t][6] then
r = d.newRect(board[t][3],board [t][6],w20,h20)
r:setFillColor(1,1,0)
r.anchorX=0
r.anchorY=0
end
end
end
end
end
Runtime:addEventListener("touch", fill)
I use new variable whichTurn to figure out what to put on board in each turn. I think code is self-explanatory. Try
...
local EMPTY, X, O = 0, 1, 2
local whichTurn = X -- X is starting game
...
--FILL COMPARTMENT W/ COLOR WHEN TOUCHED
local function fill (event)
if event.phase == "began" then
for t = 1, 9 do
if event.x > board[t][3] and event.x < board [t][5] then
if event.y < board[t][4] and event.y > board[t][6] then
if board[t][7] == EMPTY then
board[t][7] = whichTurn
--[[
The operator AND returns its first argument if it is false;
otherwise, it returns its second argument. The operator OR
returns its first argument if it is not false; otherwise, it
returns its second argument.
--]]
whichTurn = whichTurn == X and O or X
end
end
end
end
end
end
Runtime:addEventListener("touch", fill)
You can read more about differences between tap and touch events in Corona.
Check also this from Corona documentation
Filtering Multiple Taps
Using the event.numTaps property, you can easily determine whether an
object was tapped multiple times and concurrently ignore single taps
on the object.
My collision detection works by getting intersection between rectangles and reversing the effect. This is happening during each frame. It works great except in the case where the player is sitting on top of a corner and jumps. every once in a while the vertical intersection is greater than the horizontal intersection which makes my player slide down the side of the platform. Any suggestions?
-- detect initial collision
if mathy.hasCollided(player, platform) then
local playerBoundaries = player:boundaries()
-- list of intersections between platform and player
local bottomBoundary = mathy.bottomBoundary( playerBoundaries, platform )
local topBoundary = mathy.topBoundary( playerBoundaries, platform )
local rightBoundary = mathy.rightBoundary( playerBoundaries, platform )
local leftBoundary = mathy.leftBoundary( playerBoundaries, platform )
local smallestDist = ""
local smallestBoundary
local boundaries = {
bottom = bottomBoundary,
top = topBoundary,
right = rightBoundary,
left = leftBoundary
}
-- get the smallest intersection (thats the side we're probably closest to)
for direction, boundary in pairs(boundaries) do
if not smallestBoundary then
smallestBoundary = boundary
smallestDist = direction
end
if smallestBoundary > boundary then
smallestBoundary = boundary
smallestDist = direction
end
end
-- reverse effects depending on collision location
if smallestDist == "bottom" then
player.desiredPos:add(diffX, -bottomBoundary)
player.velocity.y = 0
player.onGround = true
elseif smallestDist == "top" then
player.velocity.y = 250
player.desiredPos:add(0, topBoundary)
elseif smallestDist == "right" then
player.desiredPos:add(-rightBoundary, 0)
elseif smallestDist == "left" then
player.desiredPos:add(leftBoundary, 0)
end
end
Its hard to tell from the short clip, but I think the issue is a result of checking for the smallest intersection instead of checking for the intersection in the direction of the object's velocity. You could try something like this in place of the smallestBoundary loop:
local boundary = nil
if (player.velocity.y > 0) then
boundary = topBoundary
elseif (player.velocity.y < 0) then
boundary = bottomBoundary
elseif (player.velocity.x > 0) then
boundary = rightBoundary
elseif (player.velocity.x < 0) then
boundary = leftBoundary
end
Of course, this isn't as robust as it can be. You might also try to combine the two approaches and do something like this in place of the smallestBoundary loop:
local yMod = math.abs(player.velocity.y)
local xMod = math.abs(player.velocity.x)
local topMod = player.velocity.y > 0 and yMod or 1
local bottomMod = player.velocity.y < 0 and yMod or 1
local rightMod = player.velocity.x > 0 and xMod or 1
local leftMod = player.velocity.x < 0 and xMod or 1
local boundaries = {
bottom = (bottomMod / MAX_VELOCITY) * bottomBoundary,
top = (topMod / MAX_VELOCITY) * topBoundary,
right = (rightMod / MAX_VELOCITY) * rightBoundary,
left = (leftMod / MAX_VELOCITY) * leftBoundary
}
for direction, boundary in pairs(boundaries) do
if not smallestBoundary then
smallestBoundary = boundary
smallestDist = direction
end
if smallestBoundary > boundary then
smallestBoundary = boundary
smallestDist = direction
end
end
This will do exactly what you're doing now, but will adjust the size of the boundary (in context of the comparison only) by the velocity of the player in that direction. So if you're moving in x with -5 and in y with -10, downward collisions in the y plane will have twice the weight of leftward collisions in the x plane. Of course, there's always the other option of adjusting the player in every plane of collision:
if bottomBoundary > 0 then
player.desiredPos:add(diffX, -bottomBoundary)
player.velocity.y = 0
player.onGround = true
end
if topBoundary > 0 then
player.velocity.y = 250
player.desiredPos:add(0, topBoundary)
end
if rightBoundary > 0 then
player.desiredPos:add(-rightBoundary, 0)
end
if leftBoundary > 0 then
player.desiredPos:add(leftBoundary, 0)
end
This last method would make the most sense, except you don't seem to handle collisions in all directions uniformly, for whatever reason, so it might not fit your architecture.
Keep in mind that I'm not familiar with the framework you're using, so this code might not work out of the box. Also, this post assumes that +y is up, -y is down, +x is right, and -x is left.
When I run this code (which is based on the "DragMe" sample app) I get very unexpected results in very particular circumstances.
My "snapToGrid" function sets x and y to the nearest round 100 value after a drag.
The "examine" function outputs the x and y values after an object is created, moved or rotated (by clicking on it).
If you place an object in row 5 (so y = 500) and rotate it you will see that the y value changes to 499.99996948242 but this does not happen in any other row.
How can this be explained? I notice this does not happen if the physics bodies are not added to the display object.
I know I could call snapToGrid after rotation or round the value before I use it for anything else but I think there is an important opportunity here for me to learn something useful about Corona.
local function snapToGrid(t)
modHalf = t.x % t.width
if modHalf > t.width/2 then
t.x = t.x + (t.width-modHalf)
end
if modHalf < t.width/2 then
t.x = t.x - modHalf
end
modHalfY = t.y % t.height
if modHalfY > t.height/2 then
t.y = t.y + (t.height-modHalfY)
end
if modHalfY < t.height/2 then
t.y = t.y - modHalfY
end
display.getCurrentStage():setFocus( nil )
t.isFocus = false
return true
end
function rotatePiece(target)
if target.rotation == 270 then
target.rotation = 0
else
target.rotation = target.rotation + 90
end
end
local function dragBody(event)
local target = event.target
local phase = event.phase
local halfWidth = target.width/2
local halfHeight = target.height/2
--get tileX and tileY relative to tile centre
local tileX = event.x-target.x
local tileY = event.y-target.y
local modHalf = ""
local snap = 15
if phase == "began" then
-- Make target the top-most object
display.getCurrentStage():setFocus( target )
-- Spurious events can be sent to the target, e.g. the user presses
-- elsewhere on the screen and then moves the finger over the target.
-- To prevent this, we add this flag. Only when it's true will "move"
-- events be sent to the target.
target.isFocus = true
-- Store initial position
target.x0 = event.x - target.x
target.y0 = event.y - target.y
elseif target.isFocus then
if phase == "moved" then
-- Make object move (we subtract target.x0,target.y0 so that moves are
-- relative to initial grab point, rather than object "snapping").
target.x = event.x - target.x0
target.y = event.y - target.y0
if target.x > display.contentWidth - (target.width/2) then target.x = display.contentWidth - (target.width/2) end
if target.y > display.contentHeight - (target.height/2) then target.y = display.contentHeight - (target.height/2) end
if target.x < 0 + (target.width/2) then target.x = 0 + (target.width/2) end
if target.y < 0 + (target.height/2) then target.y = 0 + (target.height/2) end
modHalf = target.x % target.width
if modHalf < snap then
target.x = target.x - modHalf
end
if modHalf > ((target.width) - snap) then
target.x = target.x + ((target.width)-modHalf)
end
modHalfY = target.y % target.height
if modHalfY < snap then
target.y = target.y - modHalfY
end
if modHalfY > ((target.height) - snap) then
target.y = target.y + ((target.height)-modHalfY)
end
hasMoved = true
return true
elseif phase == "ended" then
if hasMoved then
hasMoved = false
snapToGrid(target)
--tile has moved
examine(target)
return true
else
--rotate piece
rotatePiece(target)
display.getCurrentStage():setFocus( nil )
target.isFocus = false
--tile has rotated
examine(target)
return true
end
end
end
-- Important to return true. This tells the system that the event
-- should not be propagated to listeners of any objects underneath.
return true
end
local onTouch = function(event)
if event.phase == "began" then
local tile = {}
img = display.newRect(event.x,event.y,100,100)
img:addEventListener( "touch", dragBody )
snapToGrid(img)
--top right corner and top middle solid
topRight = {16,-16,16,50,50,50,50,-16}
--top left and left middle solid
topLeft = {-16,-16,-16,-50,50,-50,50,-16}
--bottom right and right middle solid
bottomRight = {16,16,16,50,-50,50,-50,16}
--bottom left and bottom middle solid
bottomLeft = {-16,16,-16,-50,-50,-50,-50,16}
physics.addBody( img, "static",
{shape=topRight},
{shape=topLeft},
{shape=bottomLeft},
{shape=bottomRight}
)
--new tile created
examine(img)
return true
end
end
function examine(img)
print("--------------------------------------------------")
if img ~= nil then
print("X: "..img.x..", Y: "..img.y)
end
print("--------------------------------------------------")
end
local img
local physics = require( "physics" )
physics.setDrawMode( "hybrid" )
--draw gridlines
for i = 49, display.contentHeight, 100 do
local line = display.newLine(0,i,display.contentWidth,i)
local line2 = display.newLine(0,i+2,display.contentWidth,i+2)
end
for i = 49, display.contentWidth, 100 do
local line = display.newLine(i,0,i,display.contentHeight )
local line2 = display.newLine(i+2,0,i+2,display.contentHeight )
end
--init
physics.start()
Runtime:addEventListener("touch", onTouch)
There several possibilities. Firstly, since there are no integers in Lua, all numbers are double floating point values. According to FloatingPoint on Lua wiki,
Some vendors' printf implementations may not be able to handle
printing floating point numbers accurately. Believe it or not, some
may incorrectly print integers (that are floating point numbers). This
can manifest itself as incorrectly printing some numbers in Lua.
Indeed, try the following:
for i=1,50,0.01 do print(i) end
You will see that a lot of numbers print exactly as you would expect, by many print with an error of 10^-12 or even 2 x 10^-12.
However, your x prints fine when you don't give it to physics module. So surely the physics module does some computation on object position and changes it. I would certainly expect that for dynamic objects (due to collision detection), but here your object seems to be "static". So it must be that physics adjusts x even for static objects. The adjustment is so small that it would not be visible on the screen (you can't perceive any motion less than a pixel), but you're right that it is interesting to ask why.
The SO post Lua: subtracting decimal numbers doesn't return correct precision is worth reading; it has some links you might find interesting and gives the neat example that decimal 0.01 cannot be represented exactly in base 2; just like 1/3 can't be represented exactly in base 10 (but it can in base 3: it would be 0.1 base 3!). The question shows that, although Lua (or possibly, the underlying C) is smart enough to print 0.01 as 0.01, it fails to print 10.08-10.07 as 0.01.
If you really want to shake your understanding of floating point values, try this:
> a=0.3
> b=0.3
> print(a==b)
true
> -- so far so good; now this:
> a=0.15 + 0.15
> b=0.1 + 0.2
> print(a,b)
0.3 0.3
> print(c==d)
false -- woa!
This is explained by the fact that 0.15 in binary has a small error which is different from that of 0.1 or 0.2, so in terms of bits they are not identical; although when printed, the difference is too small to show. You may want to read the Floating Point Guide.
Actually this problem only occurs if you add the physics, so I think it is fair to say that the issue is caused by transferring control to box2d, not by Corona handling of the number alone. I asked on Corona forums and got this answer.
http://forums.coronalabs.com/topic/46245-display-object-that-has-static-physics-body-moves-very-slightly-when-rotated/
I followed this tutorial to make a breakout game, but at some point, the ball keeps leaning on the top wall when the ball angle is too large ( too horizontal ). Is there any logic I can tune so that the ball can avoid this behavior?
Here is the screenshot :
The ball's related source code is:
local ballRadius = 10
ball = display.newCircle( display.contentWidth / 2, display.contentHeight / 2, ballRadius )
physics.addBody(ball, "dynamic", {friction=0, bounce = 1, radius=ballRadius})
It is some kind of weird. But I've done once like this...
Create 3 variables/flags:
local horizontalMotionFlag,yPos_1,yPos_2 = 0,0,0
Then:
wall.type = "LeftWall" -- to the LeftWall
-- and --
wall.type = "RightWall" -- to the RightWall
Add the following lines inside event.phase == "ended"
------------------------------------------------------------------------------------------
if(event.other.type == "LeftWall") then
yPos_1 = ball.y
if(horizontalMotionFlag==0)then
horizontalMotionFlag = 1
else
if(math.abs(yPos_1-yPos_2) < 50)then
print("CoHorizontal motion detected. Change angle...1")
horizontalMotionFlag = 0
ball:applyForce( 0, 1, ball.x, ball.y ) -- apply a small downward force
ball:applyForce( 0, 0, ball.x, ball.y ) -- resetting the force
-- You can also check the direction of ball and apply force to -1(upwards) also --
end
end
end
if(event.other.type == "RightWall") then
yPos_2 = ball.y
if(horizontalMotionFlag==0)then
horizontalMotionFlag = 1
else
if(math.abs(yPos_1-yPos_2) < 50)then
print("CoHorizontal motion detected. Change angle...")
horizontalMotionFlag = 0
ball:applyForce( 0, 1, ball.x, ball.y ) -- apply a small downward force
ball:applyForce( 0, 0, ball.x, ball.y ) -- resetting the force
-- You can also check the direction of ball and apply force to -1(upwards) also --
end
end
end
------------------------------------------------------------------------------------------
Then add the following line inside event.other.type == "destructible" and event.other.type == "bottomWall" :
horizontalMotionFlag = 0;
Keep Coding............. :)
So, I have been working on this game for a bit. However, over the past day I have not been able to figure out how to work my collision detection.
The scale at default is equal to 2.
The player is 41*scale by 64*scale.
My player is centered in the middle of the screen in both the x and y axis.
Since the player is centered the world is what moves, those variables are worldx and worldy. The player always stays at the center of the screen.
My tile map is stored in an array and is based on the image pixel color. If the pixel is white at map[x][y] the value is set to 0 else it's set to the block. Meaning the block does not get rendered.
for x = 0, w-1 do --scans the image and builds the map array
amap[x] = {}
for y = 0, h-1 do
local r, g, b, a = source:getPixel(x, y)
if r == 255 and g == 255 and b == 255 then
block = 0
end
if r == 255 and g == 100 and b == 0 then
block = 1
end
if r == 130 and g == 125 and b == 0 then
block = 2
end
if r == 76 and g == 76 and b == 76 then
block = 3
end
if r == 255 and g == 0 and b == 255 then
--this is the spawn pixel yet to build
end
amap[x][y] = block
end
end --end function
function that draws the map
for x = 0, w-1 do --draws the map
for y = 0, h-1 do
if amap[x][y] ~= 0 then
love.graphics.drawq(ImgBlocks, Blocks[amap[x][y]], 32*x*(3/bscale) + worldx, 32*y*(3/bscale) + worldy + jy, 0 , 3/bscale, 3/bscale)
end
if amap[x][y] == 4 then
end
end
end --end function
The function needs to return true or false base on if there is collision between player and block.
Your tiles are 32x32, correct? (from the drawq call) I would recommend you make a function that checks if a point is in a solid tile:
function pointCollisionTest(x, y)
-- find which tile the point is in
local tx, ty = math.floor(x / 32), math.floor(y / 32)
-- check the tile
if map[tx][ty] == solid then
return true
else
return false
end
end
You'll have to change the if map[x][y] == solid logic based on how you determine solid tiles, but this code should otherwise work.
Once you have point collision, the way you make the player collide is by checking each corner of its hitbox (which you should easily be able to determine) against this function whenever the player moves. There are a few ways to do this; I use the relatively simple method of calculating the player's new position, testing it, then canceling the move entirely if the collision test returns true. You have to check/cancel the x and y components of the move separately, though, so the player can move along walls instead of sticking to them.
Are you asking for a basic 2d collision detection?
A simplified formula:
if (playerx > blockminx) and (playery < blockmaxx) and (playery > blockminy) and (playery < blockmaxy) then collission