WackyToaster's Forum Posts

  • You can check the console (press f12) for a better error message. Imo this message should actually show in the error popup from construct because the error message otherwise is largely meaningless.

  • My solution would probably be to simply not cast the ray from inside the solid at all. Check this

    But there's also the possibility to use solid collision tags

  • Well this is gonna need some more tinkering. I've managed to update the script more to my workflow, so now it exports each tag as an individual zip. It's a bit rough but it works ok for now.

    The main issue I'm still facing is that cels are not the full image but it's basically the cropped sprite, which for animations causes the origin to go all over the place due to different individual frame sizes. I need to somehow figure out how to get the origin on a consistent spot despite that.

    I can easily get the cel.bounds and I also have the sprite width/height.

    aseprite.org/api/image

    I think that should be everything I need to math it out but my brain is not braining today. Anyway, here's the updated script for now.

    -- Get the active sprite
    local sprite = app.sprite
    
    if sprite == nil then
    	print("No active sprite found.")
    	return
    end
    
    local spriteName = string.match(sprite.filename, "[^/\\]+$")
    spriteName = spriteName:gsub("%..*$", "")
    
    
    -- Create a new dialog
    local dlg = Dialog("Export animation for C3 - this will briefly hang :)")
    dlg:button{ id="confirm", text="Confirm" }
    dlg:button{ id="cancel", text="Cancel" }
    dlg:show()
    
    local data = dlg.data
    
    if data.confirm then
    	-- Extract the directory path of the original sprite file
    	local originalFilePath = sprite.filename
    	
    	local outputFolder = app.fs.joinPath(string.match(originalFilePath, "^(.-)([^\\/]-%.?([^%.\\/]*))$"), "tmp-export-for-c3")
    	if outputFolder == nil then
    		print("Failed to determine output folder.")
    		return
    	end
    	
    	for i, tag in ipairs(sprite.tags) do
    		
    		-- Use the name that was set in the dialog or the file name if no name was specified in the dialog
    		local defaultAnimationName = tag.name
    		
    		-- Construct the JSON data
    		local jsonData = {
    			["use-raw-folder-names"] = true,
    			["animation"] = {
    				["name"] = tag.name,
    				["speed"] = 0,
    				["loop"] = false,
    				["ping-pong"] = false,
    				["repeat-count"] = tag.repeats or 1,
    				["repeat-to"] = 0,
    				["frame-durations"] = {},
    				["frame-tags"] = {},
    				["frame-image-points"] = {}
    			}
    		}
    		
    		if tag.aniDir <= 1 then
    			jsonData["animation"]["loop"] = true
    		else
    			jsonData["animation"]["ping-pong"] = true
    		end
    		
    		local fps = math.floor((1000 / (tag.fromFrame.duration * 1000)) + 0.5)
    		jsonData["animation"]["speed"] = fps
    		
    		-- Flatten to merge all layers
    		local dupe = Sprite(sprite)
    		dupe:flatten()
    		
    		local origin = {{
    			originX = 0.5,
    			originY = 0.5
    		}}
    		
    		for i, cel in ipairs(dupe.cels) do
    		
    			if cel.frameNumber >= tag.fromFrame.frameNumber and cel.frameNumber <= tag.toFrame.frameNumber then
    				local filename = app.fs.joinPath(outputFolder, i .. ".png")
    				
    				cel.image:saveAs(filename)
    				
    				--jsonData["frame-image-points"].inser = {"originX":3, "originY":3}
    				table.insert(jsonData["animation"]["frame-image-points"], origin)
    				
    				local lfps = math.floor((1000 / (cel.frame.duration * 1000)) + 0.5)
    				table.insert(jsonData["animation"]["frame-durations"], math.floor((fps / lfps) + 0.5))
    				end
    		end
    		
    		dupe:close()
    		
    		--app.command.Undo()
    		
    		-- Write JSON data to file
    		local jsonFilename = app.fs.joinPath(outputFolder, "c3-import-settings.json")
    		local jsonFile = io.open(jsonFilename, "w")
    		if jsonFile then
    			jsonFile:write(json.encode(jsonData))
    			jsonFile:close()
    		else
    			print("Failed to generate JSON file.")
    			return
    		end
    		
    		local pathSeparator = package.config:sub(1, 1)
    		-- Choose the appropriate zip command based on the operating system
    		local osType = (pathSeparator == "\\") and "Windows" or "Unix-based"
    		
    		local zipCmd
    		
    		local zipFilename = app.fs.joinPath(outputFolder, defaultAnimationName .. ".zip")
    		
    		if osType:find("Windows") then
    		-- Use PowerShell on Windows
    		zipCmd = 'powershell Compress-Archive -Path "' .. outputFolder .. pathSeparator .. "*" .. '" -DestinationPath "' .. zipFilename .. '" -Force'
    		else
    		-- Use the zip command on Unix-based systems
    		zipCmd = 'zip -r "' .. zipFilename .. '" "' .. outputFolder .. pathSeparator .. "*" ..'"'
    		end
    		
    		-- Execute the zip command
    		os.execute(zipCmd)
    		
    		-- Move the zip file to the new destination path
    		local directoryPath, fileName = zipFilename:match("(.+)[/\\]([^/\\]+)$")
    		local newZipFilePath = app.fs.joinPath(directoryPath, "..", fileName)
    		
    		local moveCommand
    		
    		if osType:find("Windows") then
    		-- Windows system
    		moveCommand = "move".. " " .. zipFilename .. " " .. newZipFilePath
    		else
    		-- Unix-like system (Linux, macOS, etc.)
    		moveCommand = "mv".. " " .. zipFilename .. " " .. newZipFilePath
    		end
    		
    		os.execute(moveCommand)
    		
    		-- Remove the temporary folder
    		local removeCommand
    		
    		if osType:find("Windows") then
    		-- Windows system
    		removeCommand = "rmdir /s /q" .. " " .. outputFolder
    		else
    		-- Unix-like system (Linux, macOS, etc.)
    		removeCommand = "rm -rf" .. " " .. outputFolder
    		end
    		
    		os.execute(removeCommand)
    		
    	end
    end

    Very frustrating. At leas they should allow me to see what I wrote before when I had the license! Seems not fair that what I paid for at the time is not accessible any more.

    You can open any project in the free version even if it's way above the free limits. You just cannot edit it. So you can read through your code as long as you need without having to pay a single cent. If you want to edit, that's where you'll need to buy again.

  • I figured a first attempt would miss the mark, specially because I am sure there are a few popular workflows that people like to use when making their animations. Just wanted to make something because it's easier to tweak an existing script than coming up with it from scratch.

    All good, it's a start and I can try and adapt it when I have spare time. There's no way to make a one-size-fits-all solution anyway. But having a base script to start off of is already really helpful.

    Fortunately I saw in the documentation of Aseprite, that you can assign custom data to frames. That custom information could be used by the exporter to generate C3 tags for each frame.

    That's good news. Probably also useful for adding origin/imagepoint data and such! My ideal would be that I'm able to input practically all the data of a sprite in aseprite, generate a zip (or multiple) and drag & drop it into Construct which then updates all animations with the new settings/data.

    This means some initial extra work but once it's all set up I should be able to iterate on the animations really quickly, and it's all packaged very neatly.

  • I see. I have used both mixins and composition before. I found mixins to be kinda weird and awkward though, so composition is fine. Thanks :)

  • I should post this here too while I'm at it.

    Subscribe to Construct videos now
  • Hmmm

    In any case if you're legit I recommend running away from this project as fast as possible because Nintendo is going to DMCA you quicker than you can catch your first pokemon.

  • Only have physics run on the host! Sync the objects position, angle etc but have physics disabled for the peer. If you simulate the physics on both machines, even miniscule differences in CPU timing and rounding errors will have the objects often diverge.

  • You do not have permission to view this post

  • Should have bolded and quoted "RELATIVELY".

    Haha yeah. Thank you so much! I'll try it out asap.

    There is no explicit FPS for the whole animation, instead each frame has a time in milleseconds. To fill in a value I calculate a tentative FPS from the duration of the first frame and then use that value to calculate durations for the rest of the frames which are relative to that first measure.

    That is fine. That is one of the settings that I often end up changing anyway. Often I end up importing an animation and it ends up faster or slower than I have set it up in aseprite simply because in the context of the entire game it feels different. What is interesting is really the individual frame-timing!

    The way Aseprite uses settings such as Loop is per tag, so you can have multiple different ones in ranges of the whole timeline. C3 just has one value for the whole animation. So instead of using the values from Aseprite, the export script just shows a dialog with the settings that can't be extracted in a way that makes sense.

    The way I use tags is to have multiple animations in one file. In my ideal world I would export each tag from aseprite into a different animation. Here's an example from the net of how I roughly set up my files. So each tags loop settings would be per animation, and each tag would export a different animation for the same sprite.

    i.ytimg.com/vi/KzgugqLahik/maxresdefault.jpg

    The export script can use the tag information, but not completely because Aseprite is more advanced that C3 in that regard. In C3 you only have a tag for each frame, that's it. In Aseprite you can define a range. To do something useful with the data, the frames where a tag range starts, get the value, while frames covered by the range, are left empty. The information could be used differently.

    See above, I'd use the frame tag as an individual animation. Tags in the context of aseprite are used differently than in Construct. That would mean though there is no real place to actually put tags as they are used in Construct, but that's a fair tradeoff. I'll see if I can manage to adapt the script.

  • On start of layout set your global variable to yourobjectname.count

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • I think I figured it out. The CanvasToLayer expression was the key.

    wackytoaster.at/parachute/resize.c3p

  • Here is another solution - click any point on the sprite:

    https://www.dropbox.com/scl/fi/drhkya67euorlv2iq1pqr/rotate_easypeasy.c3p?rlkey=eb74yo7bqm398j8oqt1kdcy2o&dl=0

    Heh, smart solution. Not sure how well it would work with the player in motion though.

  • I think the easiest solution would be to have a separate double-jumping animation with a different origin point.