DiegoM's Recent Forum Activity

  • The setting was moved into the Animations editor.

    The Animations editor now has an additional button in the top right (next to the close button), that brings up a dialog with some miscellaneous settings.

  • I'm afraid not. I looked at what the API had to offer earlier today, because I thought that was going to be something you were going to want to do, but there is no such thing.

    Snapping to the path would be a neat feature.

    What I thought would be possible with the available functionality is to follow the path when the playable character is in the half pipe, and when you reach the edges, use a different motion to go up and then come back down. Dunno of that made sense.

  • In this case the timeline editor is only used to set up the curved path, at runtime the path is used by the MoveTo behaviour so you can move an instance along it.

    The keyframe information is not used at all.

  • The change I was suspecting caused the problem... was the culprit. It was an attempt to fix github.com/Scirra/Construct-bugs/issues/7261. Even though the fix is quite comprehensive when it comes to tweens with mirrored and flipped instances in a hierarchy, it completely misses timelines.

    Our aim is to avoid regressions in stable releases, so that change will be reverted for the stable and will try again in the next beta cycle to get everything working properly.

  • At the moment there is no convenient way of doing this in the editor, and the runtime doesn't have anything for this specific purpose either.

    You can try printing to console all the values of each node as you traverse a flowchart at runtime. That is not ideal because you would need to make sure to manually visit all nodes. If you decide to do this, use the On any node entered trigger to detect when a node is reached in combination with the For Each Output loop condition to loop through all the outputs of the node. In the action block use the FlowchartController.CurOutputName[/b and FlowchartController.CurOutputValue expressions to get the name and value of each output.

    If you feel comfortable looking at JSON files, you can also try looking directly at the saved information of the project file.

    Once you made the backup, do the following with it:

    1. Rename from .c3p to .zip
    2. Extract it to a folder, like you would any other zip file
    3. Look for the flowcharts folder
    4. In it there is a .json file for each flowchart, ignore the .uistate.json files
    5. There is a lot of stuff in each of those files, but what you are looking for is the various "outputs" entries. Each of those have all the outputs of each node. You can copy and paste what you need into a different file, or just delete everything that is not what you are looking for. That is why we do this kind of things in a backup file!

    Let me know if you need any help with any of that.

  • When you select a node, look in the Properties bar, there is a "Link mode" property for each output in a node.

    Do mind that this feature helps a little bit with the general look of a flowchart when there are a lot of connections, but in my experience, it isn't a full proof solution, that is why it is not activated by default for all outputs.

    I find it works well for the cases you end up with links that go over other nodes, if that is the case, it can help things look tidier. If the straight line links don't go over other nodes... it's probably best to leave them as is.

  • In r380 there was one change to fix another issue which might be causing this problem. Since we are getting close to a stable release, I think I will be reverting that change.

    Can you share a minimal project the reproduces your problem?

  • You might be able to get around that by creating a temporary image, rather than using the one from the cel.

    aseprite.org/api/image

    I am thinking of creating a new image, making it the size of the frame so it includes all the empty space, and then use image.drawImage to paste the original one in the correct position. Not sure where to take the "correct" position from though, but it should be there somewhere.

    The position should be Cel.position

    aseprite.org/api/cel

  • I haven't looked closely enough to the documentation or even tried anything but I think that making an extension to visually edit image points and collision polygons should be possible since there is functionality to draw custom things on a canvas in a dialog, as well as access to mouse events... it does sound like a ton of work though, so I don't think I will be doing that.

    I might do some experiments to figure out if it is possible or not.

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • 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.

    Generating a unique zip file for each tag sounds like it would fit in nicely with what the API has to offer. That would prevent using the tag information for C3 though. 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.

  • Should have bolded and quoted "RELATIVELY".

    Anyway, I got interested in this and cobbled this:

    -- 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")
    dlg:entry{ id="animationName", label="Animation name:", text=spriteName }
    dlg:check{ id="loop", label="Loop" }
    dlg:check{ id="pingPong", label="Ping Pong" }
    dlg:number{ id="repeatCount", label="Repeat count:", text=string.format("%i", 1), decimals=0 }
    dlg:number{ id="repeatTo", label="Repeat to:", text=string.format("%i", 0), decimals=0 }
    dlg:button{ id="ok", text="OK", focus=true }
    dlg.bounds = Rectangle(dlg.bounds.x - 50, dlg.bounds.y, dlg.bounds.width + 100, dlg.bounds.height)
    dlg:show()
    
    -- Get the data from the dialog
    local data = dlg.data
    
    -- Check if the user cancelled the dialog
    if data == nil then
    	print("Export canceled by user.")
    	return
    end
    
    if not data.ok then
    	print("Export canceled by user.")
    	return
    end
    
    -- 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
    
    -- Use the name that was set in the dialog or the file name if no name was specified in the dialog
    local defaultAnimationName = data.animationName and data.animationName or spriteName
    
    -- Construct the JSON data
    local jsonData = {
    	["use-raw-folder-names"] = true,
    	["animation"] = {
    		["name"] = defaultAnimationName,
    		["speed"] = 0,
    		["loop"] = data.loop,
    		["ping-pong"] = data.pingPong,
    		["repeat-count"] = data.repeatCount and data.repeatCount or 1,
    		["repeat-to"] = data.repeatTo and data.repeatTo or 0,
    		["frame-durations"] = {},
    		["frame-tags"] = {}
    	}
    }
    
    local frameTags = {}
    
    for i, tag in ipairs(sprite.tags) do
    	local frame = tag.fromFrame;
    	frameTags[frame.frameNumber] = tag.name
    end
    
    local fps = math.floor((1000 / (sprite.frames[1].duration * 1000)) + 0.5)
    
    jsonData["animation"]["speed"] = fps
    
    -- Flatten to merge all layers
    sprite:flatten()
    
    for i, cel in ipairs(sprite.cels) do
    	local filename = app.fs.joinPath(outputFolder, i .. ".png")
    
    	cel.image:saveAs(filename)
    
    	local lfps = math.floor((1000 / (cel.frame.duration * 1000)) + 0.5)
    
    	table.insert(jsonData["animation"]["frame-durations"], math.floor((fps / lfps) + 0.5))
    
    	local frameTag = frameTags[cel.frame.frameNumber] and frameTags[cel.frame.frameNumber] or ""
    
    	table.insert(jsonData["animation"]["frame-tags"], frameTag)
    end
    
    -- Undo the flatten done earlier
    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)
    

    That produces a zip file with all the images and the JSON so C3 knows what to do with it.

    I found a couple of issue which can not be reconciled:

    1. 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.
    2. 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.
    3. 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.
    4. The script flattens all the layers to export all the image information. This could be done differently, like for instance letting the script just export a specified layer.

    You can try it out and see if it's useful to you.

  • I don't know anything about noise, but I did a little bit of digging and found what looks like a rather elegant solution for your problem, so elegant that I wouldn't have been able to figure it out in a million years :P

    gamedev.net/blog/33/entry-2138456-seamless-noise

    Towards the end of the article there is this bit of code:

    for x=0,bufferwidth-1,1 do
    	for y=0,bufferheight-1,1 do
    		local s=x/bufferwidth
    		local t=y/bufferheight
    		local dx=x2-x1
    		local dy=y2-y1
    
    		local nx=x1+cos(s*2*pi)*dx/(2*pi)
    		local ny=y1+cos(t*2*pi)*dy/(2*pi)
    		local nz=x1+sin(s*2*pi)*dx/(2*pi)
    		local nw=y1+sin(t*2*pi)*dy/(2*pi)
    
    		buffer:set(x,y,Noise4D(nx,ny,nz,nw))
    	end
    end
    

    I am not too sure what language is that, but whatever it is, it should be easy to translate it to JavaScript using simplex-noise.js

    // Going to need 4D noise
    import { createNoise4D } from "./simplex-noise.js";
    
    const noise4D = createNoise4D();
    
    // The size of the noise data we are going to generate
    const width = 250;
    const height = 250;
    
    // Shortest way I found to declare a 2D array to store the result
    const noiseTexture = Array.from({ length: width }, () => new Array(height).fill(0));
    
    for (let x=0; width-1; x++)
    {
    	for (let y=0; height-1; y++)
    	{
    		// Do some sick math here!
    		let s = x / width;
    		let t = y / height;
    		let dx = x2-x1;
    		let dy = y2-y1;
    
    		let nx = x1 + Math.cos(s*2*Math.PI) * dx / (2*Math.PI);
    		let ny = y1 + Math.cos(t*2*Math.PI) * dy / (2*Math.PI);
    		let nz = x1 + Math.sin(s*2*Math.PI) * dx / (2*Math.PI);
    		let nw = y1 + Math.sin(t*2*Math.PI) * dy / (2*Math.PI);
    		
    		// Store the result for each coordinate
    		noiseTexture[x][y] = noise4D(nx,ny,nz,nw);
    	}
    }
    

    That should produce seamless noise data you can use elsewhere.

    I got the link to the article from one of the answers found here:

    gamedev.stackexchange.com/questions/23625/how-do-you-generate-tileable-perlin-noise

    The snippet is explained in "simple" terms in there

    Basically, map the X coordinate of your pixel to a 2D circle, and the Y coordinate of your pixel to a second 2D circle, and place those two circles orthogonal to each other in 4D space. The resulting texture is tileable, has no obvious distortion, and doesn't repeat in the way that a mirrored texture would.

    That should be obvious XD

    By the way, I haven't tried any of this, but it looks like it might just work!

DiegoM's avatar

DiegoM

Member since 24 Apr, 2015

Twitter
DiegoM has 1,379,812 followers

Trophy Case

  • 9-Year Club
  • Jupiter Mission Supports Gordon's mission to Jupiter
  • Forum Contributor Made 100 posts in the forums
  • Forum Patron Made 500 posts in the forums
  • Regular Visitor Visited Construct.net 7 days in a row
  • RTFM Read the fabulous manual
  • Email Verified

Progress

15/44
How to earn trophies

Blogs