Well i have some trouble removing an object from the game, the thing is that i have a player class (made out of a metatable), inside it i have a variable called sprite that holds the address for the image sprite i will draw onScreen, so when i create the object i don't draw the sprite right away, for that i made a function draw (this is just to explain what i have). in the game.lua i draw the player by calling that function, and afterwards i want to delete my instance of player (so that way the image onscreen dissapears also)... thats all, i tried player:removeSelf(), display.remove(player) and one of them threw me an error (attemp to call field 'removeSelf' (a nil value)) and the other one runs fine but it doesn't change the fact that the player is still there (i can acces it's functions and the sprite is still shown onscreen... well here is my code:
**********************************************************
game.lua:
**********************************************************
---------------------------------------------------------------------------------
-- BEGINNING OF YOUR IMPLEMENTATION
---------------------------------------------------------------------------------
local _W, _H = display.contentWidth * 0.5, display.contentHeight * 0.5
local background, player, land
local spriteSizeX, spriteSizeY = 60,70
-- Called when the scene's view does not exist:
function scene:createScene( event )
local group = self.view
local BG = display.newGroup()
local stage = display.newGroup()
local foreground = display.newGroup()
group:insert(BG)
group:insert(stage)
group:insert(foreground)
-----------------------------------------------------------------------------
-- CREATE display objects and add them to 'group' here.
-- Example use-case: Restore 'group' from previously saved state.
-----------------------------------------------------------------------------
background = display.newImage("assets/backgrounds/oliveBackground.png", true)
background.x, background.y = _W, _H
BG:insert( background )
player = playerClass.new()
player:draw(15,57.5,foreground)
--player:movePlayer(300,140)
terrain = {}
local m,j = 0,0
for i = 1, 16 do
local l = 1
for k = 3, 10 do
land = landClass.new({posx = i, posy = k})
table.insert(terrain, land)
m = m +1
terrain[m]:draw( spriteSizeX/4 + ((spriteSizeX/2) * j), spriteSizeY/4 + ((spriteSizeY/2) * l) + 5, stage)
l = l + 1
end
j = j+1
end
-- remove an asset doesn't work
--display.remove(terrain[1]:getSprite())
--terrain[1].removeSelf()
-- terrain[1] = nil
player:destroy()
end
**********************************************************
player.lua:
**********************************************************
-- player class
local player = {}
local player_mt = { __index = player }
--[[
-- attributes
local sprite, coins, speed
]]--
function player.new() -- constructor
local newPlayer = {
sprite = "assets/char/miner.png",
coins = 1000,
speed = 1
}
return setmetatable( newPlayer, player_mt )
end
-- local function, works only when called from inside this class
local function getName()
-- print("")
end
function player:draw(x,y,group)
sprite = display.newImage(self.sprite)
sprite.x, sprite.y = x, y
sprite.xScale, sprite.yScale = 0.5, 0.5
group:insert(sprite)
end
function player:movePlayer(posx,posy)
transition.to(sprite, { x = posx, y = posy, time=500, delay=0, alpha=1.0 })
end
function player:destroy()
-- none of them work
-- self.sprite = nil
-- self.sprite.removeSelf()
end
return player
After creating your sprite using display.newImage, you did not store it in the instance.
self.sprite just has the string value "assets/char/miner.png"
in your draw function add
self.spriteObject = sprite
and in your destroy function ,
self.spriteObject:removeSelf()
You can use one of these functions for your related object:
removeSelf()
destroy()
Related
I have a "parent" player scene, and I inherit scenes for each player. The parent player scene has a camera. When the game switches between players, one player turns off its camera, and the other player turns its camera on:
if state != State.ACTIVE:
# If this player is becoming active, also
# set camera current
state = State.ACTIVE
camera.current = true
else:
# If player is not becoming active,
# disable this players camera
camera.current = false
But players can be in different positions, so the camera "jumps" from one to the other. Can we do something more sophisticated, like set the new camera to the current position so the smooth setting can be used to handle the transition?
One idea is to do get_viewport().get_camera() to find the current position of the camera to try and sync the position of the current camera with the new camera that is about to turn on, but appears to not work for 2D scenes. CF: https://github.com/godotengine/godot/pull/38317
Sadly, as you found out, there is no way to get the current Camera2D in Godot 3.x. And you found the pull request that adds the feature to Godot 4.0.
What I'm going to suggest is to have one sole Camera2D, so that one is always the current one. And you can define Position2D inside your scenes that can serve as interpolation targets to move the Camera2D.
I have an script that I think will be useful for you (I made it to be RemoteTransform2D but backwards, it does push a transform, it pulls it), I call it anchor_transform_2d.gd:
tool
class_name AnchorTransform2D
extends Node2D
export var anchor_path:NodePath setget set_anchor_path
export var reference_path:NodePath setget set_reference_path
export var set_local_transform:bool
export(int, FLAGS, "x", "y") var translation_mode:int
export(int, FLAGS, "x", "y") var scale_mode:int
export var rotation_mode:bool
var _anchor:Node2D
var _reference:Node2D
func _physics_process(_delta: float) -> void:
if not is_instance_valid(_anchor) or Engine.editor_hint:
set_physics_process(false)
return
#INPUT
var input := _anchor.global_transform
if is_instance_valid(_reference):
input = _reference.global_transform.affine_inverse() * input
#TRANSLATION
var origin := Vector2 (
input.origin.x if translation_mode & 1 else 0.0,
input.origin.y if translation_mode & 2 else 0.0
)
#ROTATION
var angle := 0.0
if rotation_mode:
angle = input.get_rotation()
#SCALE
var source_scale = input.get_scale()
var scaling := Vector2 (
source_scale.x if scale_mode & 16 else 1.0,
source_scale.y if scale_mode & 32 else 1.0
)
#RESULT
_set_target_transform(
Transform2D(angle, origin) * Transform2D.IDENTITY.scaled(scaling)
)
func set_anchor_path(new_value:NodePath) -> void:
anchor_path = new_value
if not is_inside_tree():
yield(self, "tree_entered")
_anchor = get_node_or_null(anchor_path) as Node2D
set_physics_process(is_instance_valid(_anchor) and not Engine.editor_hint)
if Engine.editor_hint:
update_configuration_warning()
func set_reference_path(new_value:NodePath) -> void:
reference_path = new_value
if not is_inside_tree():
yield(self, "tree_entered")
_reference = get_node_or_null(reference_path) as Node2D
func _set_target_transform(new_value:Transform2D) -> void:
if set_local_transform:
transform = new_value
return
global_transform = new_value
func _get_configuration_warning() -> String:
if _anchor == null:
return "Anchor not found"
return ""
Add this attached to a Node2D in anchor_path set the target from which you want to pull the transform (anchor_path is a NodePath, you can set to it something like $Position2D.get_path()). And set what do you want to copy (you can choose any combination of position x, position y, scaling x, scaling y, and rotation). Then put the Camera2D as a child of the AnchorTransform2D, and set smoothing_enabled to true.
Rundown of the properties:
anchor_path: A NodePath pointing to the Node2D you want to pull the transform from.
reference_path: A NodePath pointing to a Node2D used to make the transform relative (you will be taking the transform of what you put in anchor_path relative to what you put in reference_path).
set_local_transform: Set to true if you want to pull the transform as local (relative to the parent of AnchorTransform2D), leave to false to set the global transform instead.
translation_mode: Specifies if you are going to copy the x position, y position, both or neither.
scale_mode: Specifies if you are going to copy the x scale, y scale, both or neither.
rotation_mode: Specifies if you are going to copy the rotation or not.
The only reason the script is a tool script is to give you a warning in the editor if you forgot to set the anchor_path.
I have created the following method to outline an 2D polygon using an Line2D node (in favor of _drawing because of texturing and round jointing capabilities of the Line2D node):
func Set_Polygon_Outline(_polygon_node: Node2D, _width: int = 5, _color: Color = Color.black, _texture: Texture = null) -> void:
if _polygon_node is Polygon2D:
var _polygon: PoolVector2Array = (_polygon_node as Polygon2D).polygon
if _polygon.size() >= 3:
# Line2D node setup
var _line_node: Line2D = null
var _line_name: String = str(_polygon_node.name, "_Line")
if not _polygon_node.has_node(_line_name):
_line_node = Line2D.new() ; _line_node.name = _line_name ; _polygon_node.add_child(_line_node)
else: _line_node = _polygon_node.get_node(_line_name) as Line2D
# Line2D properties setup
if _line_node != null:
_line_node.width = _width ; _line_node.default_color = _color ; _line_node.joint_mode = Line2D.LINE_JOINT_ROUND
if _texture != null:
_line_node.texture = _texture ; _line_node.texture_mode = Line2D.LINE_TEXTURE_STRETCH
var _points: PoolVector2Array = _polygon ; _points.append(_polygon[0]) ; _line_node.points = _points
How would it be possible to replicate the round point jointing on point 0 in the same way as the other points?
The result meets expectations except on the closing points (from 4 to 0)
One approach I have tried is appending an additional point (point 1) to the _points Array. While the un-textured one looks as desired, the textured variant is slightly off on the additional line because of the two superimposing textures with alpha values making it look "bolder".
Another (very unorthodox approach) is creating two polygons: one black and one with an blur shader, using the following method:
func Set_Polygon_Shadow(_polygon_node: Node2D, _size: float = 10.0, _color: Color = Color.black) -> void:
if _polygon_node is Polygon2D:
var _polygon: PoolVector2Array = (_polygon_node as Polygon2D).polygon
if _polygon.size() >= 3:
# Shadow Polygon node setup
var _shadow_name: String = str(_polygon_node.name, "_Shadow")
if not _polygon_node.has_node(_shadow_name):
var _shadow_node: Polygon2D = Polygon2D.new()
_shadow_node.polygon = Geometry.offset_polygon_2d(_polygon, _size).front() ; _shadow_node.color = _color
_shadow_node.show_behind_parent = true ; _polygon_node.add_child(_shadow_node)
# Blur Polygon node setup
var _blur_node: Polygon2D = Polygon2D.new()
_blur_node.polygon = Geometry.offset_polygon_2d(_polygon, _size * 2.0).front()
_blur_node.material = ShaderMaterial.new()
_blur_node.material.shader = preload("res://shaders/Shader_Blur.shader")
_blur_node.material.set_shader_param("Strength", 2.0)
_blur_node.show_behind_parent = true ; _polygon_node.add_child(_blur_node)
The shader code:
shader_type canvas_item;
uniform float Strength : hint_range(0.0, 5.0);
void fragment() {COLOR = textureLod(SCREEN_TEXTURE, SCREEN_UV, Strength);}
The result looks good but I can't possibly imagine using this approach on a large number of polygons.
Thank you kindly,
Mike.
If using a Line2D is a viable solution, except for fixing the loop, then let us fix the loop.
For a Line2D to loop seamless, even with transparency, it must close in a straight segment (not a corner). And have no end caps.
Thus, I suggest to move the first point, half way between its original position and the second point. Then you add at the end the original position of the first point, following by a copy of its moved position...
Like this:
# Let o be a copy of the original first point
var o = _points[0]
# Let m be the middle of the straight segment between the first and second points
var m = o + (_points[1] - o) * 0.5
_points[0] = m # The line now starts in m, we are moving the first point forth
_points.append(o) # Since we moved the first point forth, add its original position back
_points.append(m) # Add m at the end, so it loops, in the middle of a straight segment
That should result in a Line2D that loops seamlessly.
I'm working with Corona and Lua script.
basically I want to scale and rotate a 'larger than screen' image with dual-touch
Please help :)
Try this
-- one more thing
-- turn on multitouch
system.activate("multitouch")
-- which environment are we running on?
local isDevice = (system.getInfo("environment") == "device")
-- returns the distance between points a and b
function lengthOf( a, b )
local width, height = b.x-a.x, b.y-a.y
return (width*width + height*height)^0.5
end
-- returns the degrees between (0,0) and pt
-- note: 0 degrees is 'east'
function angleOfPoint( pt )
local x, y = pt.x, pt.y
local radian = math.atan2(y,x)
local angle = radian*180/math.pi
if angle < 0 then angle = 360 + angle end
return angle
end
-- returns the degrees between two points
-- note: 0 degrees is 'east'
function angleBetweenPoints( a, b )
local x, y = b.x - a.x, b.y - a.y
return angleOfPoint( { x=x, y=y } )
end
-- returns the smallest angle between the two angles
-- ie: the difference between the two angles via the shortest distance
function smallestAngleDiff( target, source )
local a = target - source
if (a > 180) then
a = a - 360
elseif (a < -180) then
a = a + 360
end
return a
end
-- rotates a point around the (0,0) point by degrees
-- returns new point object
function rotatePoint( point, degrees )
local x, y = point.x, point.y
local theta = math.rad( degrees )
local pt = {
x = x * math.cos(theta) - y * math.sin(theta),
y = x * math.sin(theta) + y * math.cos(theta)
}
return pt
end
-- rotates point around the centre by degrees
-- rounds the returned coordinates using math.round() if round == true
-- returns new coordinates object
function rotateAboutPoint( point, centre, degrees, round )
local pt = { x=point.x - centre.x, y=point.y - centre.y }
pt = rotatePoint( pt, degrees )
pt.x, pt.y = pt.x + centre.x, pt.y + centre.y
if (round) then
pt.x = math.round(pt.x)
pt.y = math.round(pt.y)
end
return pt
end
-- calculates the average centre of a list of points
local function calcAvgCentre( points )
local x, y = 0, 0
for i=1, #points do
local pt = points[i]
x = x + pt.x
y = y + pt.y
end
return { x = x / #points, y = y / #points }
end
-- calculate each tracking dot's distance and angle from the midpoint
local function updateTracking( centre, points )
for i=1, #points do
local point = points[i]
point.prevAngle = point.angle
point.prevDistance = point.distance
point.angle = angleBetweenPoints( centre, point )
point.distance = lengthOf( centre, point )
end
end
-- calculates rotation amount based on the average change in tracking point rotation
local function calcAverageRotation( points )
local total = 0
for i=1, #points do
local point = points[i]
total = total + smallestAngleDiff( point.angle, point.prevAngle )
end
return total / #points
end
-- calculates scaling amount based on the average change in tracking point distances
local function calcAverageScaling( points )
local total = 0
for i=1, #points do
local point = points[i]
total = total + point.distance / point.prevDistance
end
return total / #points
end
-- creates an object to be moved
function newTrackDot(e)
-- create a user interface object
local circle = display.newCircle( e.x, e.y, 50 )
-- make it less imposing
circle.alpha = .5
-- keep reference to the rectangle
local rect = e.target
-- standard multi-touch event listener
function circle:touch(e)
-- get the object which received the touch event
local target = circle
-- store the parent object in the event
e.parent = rect
-- handle each phase of the touch event life cycle...
if (e.phase == "began") then
-- tell corona that following touches come to this display object
display.getCurrentStage():setFocus(target, e.id)
-- remember that this object has the focus
target.hasFocus = true
-- indicate the event was handled
return true
elseif (target.hasFocus) then
-- this object is handling touches
if (e.phase == "moved") then
-- move the display object with the touch (or whatever)
target.x, target.y = e.x, e.y
else -- "ended" and "cancelled" phases
-- stop being responsible for touches
display.getCurrentStage():setFocus(target, nil)
-- remember this object no longer has the focus
target.hasFocus = false
end
-- send the event parameter to the rect object
rect:touch(e)
-- indicate that we handled the touch and not to propagate it
return true
end
-- if the target is not responsible for this touch event return false
return false
end
-- listen for touches starting on the touch layer
circle:addEventListener("touch")
-- listen for a tap when running in the simulator
function circle:tap(e)
if (e.numTaps == 2) then
-- set the parent
e.parent = rect
-- call touch to remove the tracking dot
rect:touch(e)
end
return true
end
-- only attach tap listener in the simulator
if (not isDevice) then
circle:addEventListener("tap")
end
-- pass the began phase to the tracking dot
circle:touch(e)
-- return the object for use
return circle
end
-- spawning tracking dots
-- create display group to listen for new touches
local group = display.newGroup()
-- populate display group with objects
local rect = display.newRect( group, 200, 200, 200, 100 )
rect:setFillColor(0,0,255)
rect = display.newRect( group, 300, 300, 200, 100 )
rect:setFillColor(0,255,0)
rect = display.newRect( group, 100, 400, 200, 100 )
rect:setFillColor(255,0,0)
-- keep a list of the tracking dots
group.dots = {}
-- advanced multi-touch event listener
function touch(self, e)
-- get the object which received the touch event
local target = e.target
-- get reference to self object
local rect = self
-- handle began phase of the touch event life cycle...
if (e.phase == "began") then
print( e.phase, e.x, e.y )
-- create a tracking dot
local dot = newTrackDot(e)
-- add the new dot to the list
rect.dots[ #rect.dots+1 ] = dot
-- pre-store the average centre position of all touch points
rect.prevCentre = calcAvgCentre( rect.dots )
-- pre-store the tracking dot scale and rotation values
updateTracking( rect.prevCentre, rect.dots )
-- we handled the began phase
return true
elseif (e.parent == rect) then
if (e.phase == "moved") then
print( e.phase, e.x, e.y )
-- declare working variables
local centre, scale, rotate = {}, 1, 0
-- calculate the average centre position of all touch points
centre = calcAvgCentre( rect.dots )
-- refresh tracking dot scale and rotation values
updateTracking( rect.prevCentre, rect.dots )
-- if there is more than one tracking dot, calculate the rotation and scaling
if (#rect.dots > 1) then
-- calculate the average rotation of the tracking dots
rotate = calcAverageRotation( rect.dots )
-- calculate the average scaling of the tracking dots
scale = calcAverageScaling( rect.dots )
-- apply rotation to rect
rect.rotation = rect.rotation + rotate
-- apply scaling to rect
rect.xScale, rect.yScale = rect.xScale * scale, rect.yScale * scale
end
-- declare working point for the rect location
local pt = {}
-- translation relative to centre point move
pt.x = rect.x + (centre.x - rect.prevCentre.x)
pt.y = rect.y + (centre.y - rect.prevCentre.y)
-- scale around the average centre of the pinch
-- (centre of the tracking dots, not the rect centre)
pt.x = centre.x + ((pt.x - centre.x) * scale)
pt.y = centre.y + ((pt.y - centre.y) * scale)
-- rotate the rect centre around the pinch centre
-- (same rotation as the rect is rotated!)
pt = rotateAboutPoint( pt, centre, rotate, false )
-- apply pinch translation, scaling and rotation to the rect centre
rect.x, rect.y = pt.x, pt.y
-- store the centre of all touch points
rect.prevCentre = centre
else -- "ended" and "cancelled" phases
print( e.phase, e.x, e.y )
-- remove the tracking dot from the list
if (isDevice or e.numTaps == 2) then
-- get index of dot to be removed
local index = table.indexOf( rect.dots, e.target )
-- remove dot from list
table.remove( rect.dots, index )
-- remove tracking dot from the screen
e.target:removeSelf()
-- store the new centre of all touch points
rect.prevCentre = calcAvgCentre( rect.dots )
-- refresh tracking dot scale and rotation values
updateTracking( rect.prevCentre, rect.dots )
end
end
return true
end
-- if the target is not responsible for this touch event return false
return false
end
-- attach pinch zoom touch listener
group.touch = touch
-- listen for touches starting on the touch object
group:addEventListener("touch")
Google search for "corona sdk zoom": implementing pinch zoom rotate
What I am trying to do is create a handwriting application which allows a person to press on the object (circle) and move it up/down on a set path. If the user reaches the lowest point another circle object is created and another set of points is created to follow etc.
So far I have a onTouch event which moves my ImageView object (circle) to where ever the finger is on the touch screen.
https://gist.github.com/Temptex/9796403
How can i get my ImageView (Circle) object to go from Point A -> B smoothly using onTouch events?
Edit: A picture example of what I am asking: http://imgur.com/kjgGcba
created an algorithm for it.
Created an array of x integers and y integers and used a for loop to check if the object was to high (out of bounds in the y coordinates) and check if y and x were out of bounds in the arrays.
If it was out of bounds from the array of integers, set it to the current x/y array values. in the for loop.
If this helps anyone here is the code:
private final int yCoOrdinate[] = {310, 360, 410};
private final int xCoOrdinate[] = {905, 890, 875};
// Get finger position
int x = (int)event.getRawX();
int y = (int)event.getRawY();
// View x/y co-ordinate on TextView
coordinates.setText("X = " + x + " - Y = " + y);
for(int i = 0; i < yCoOrdinate.length; i++) {
// If object is to high set it to yCoOrdinate[0]
if(y <= yCoOrdinate[0]) {
y = yCoOrdinate[0];
}
// Checks top left corner of A
if(y == yCoOrdinate[0] && x <= xCoOrdinate[0]) {
x = xCoOrdinate[0];
Log.d("Coordinate check", "X = " + x + "Y = " + y);
// Checks if current x/y position if out of bounds and sets them to i
} else if (y <= yCoOrdinate[i] && x <= xCoOrdinate[i]) {
y = yCoOrdinate[i];
x = xCoOrdinate[i];
Log.d("Coordinate check", "X = " + x + "Y = " + y);
}
}
I can now control my ImageView with in bounds.
I have random spawned objects that automatically moves across the screen. I want it so that when the objects reach a certain x position it despawns itself.
local mRandom = math.random
local objects = {"Vehicle11" ,"Vehicle21","Vehicle31","Vehicle41"}
local objectTag = 0
local object = {}
local function spawncarright()
local rightcar = {408,312}
objectTag = objectTag + 1
local objIdx = mRandom(#objects)
local objName = objects[objIdx]
object[objectTag] = display.newImage(objName..".png") -- see the difference here
object[objectTag].x = 32
object[objectTag].y = rightcar[math.random(1,2)]
object[objectTag].name = objectTag
transition.to(object[objectTag], {time = 3500, x = 348})
end
timer.performWithDelay(2000,spawncarright,0)
so once reaches object[objectTag].x = 348 the object despawn
Try this:
local function deSpawn()
for i=1,objectTag do
if(object[i]~=nil and object[i].x~=nil and object[i].x>=348)then
-- If you want to remove the object, then use the following 2 lines --
object[i]:removeSelf()
print("Removed object["..i.."]")
--or else if you want to reposition the object, then uncomment the following --
--[[
spawncarright()
--]]
end
end
end
Runtime:addEventListener("enterFrame",deSpawn)
Keep coding................... :)
You should do it within the transition.to call:
object[objectTag].deleteSelf = function(self)
object[self.name] = nil -- Remove reference to object in table
display.remove(self)
self = nil
end
local localObj = object[objectTag] -- Do this so the object doesn't change with the objectTag does; if objectTag is incremented, then when the transition ends, it won't be pointing to the same object when we call the function
transition.to(localObj, {time = 3500, x = 348, onComplete = function() localObj:deleteSelf() end})