I spent around 30 minutes working on gathering contest info from codeforces and making a todo list.
the shortcuts were pretty easy to implement, because they’re just links after all. the calendar was a bit hard to implement but I think it turned out looking pretty good. The todo list was also pretty easy, because it’s just a matter of storing stuff in local storage. As for the codeforces contests, I just grabbed contest information and took the date information and reformatted it into a date and time.
I’m making a website that lets you quickly look at codeforces stats, cuz I’ve recently rebegun my codeforces grinding.
I’m coding this in Rust because I want to practice using it, and so I’ve been working on gathering the information from the Rust API. Right now, I’m using some structs to store the data.
To make this website look less bland, I’ve added some cards of some of the things I like in the background. The top row goes from right to left and the bottom row goes from left to right. It became a little distracting, so I decided to add a small half transparent black rectangle on top of them to dim it. I’ve also set overflow to hidden so there’s no scroll. I might add projects later, but I always thought that was unnecessary.
Oh wait, I also changed my profile picture on a lot of platforms, so I had to update the one on my website.
I started remodeling my personal site. I think this time, I’m focusing more on a minimalistic theme (because I’m bad at designing so this gives me a way to avoid having to do web design heh AND it is also super easy to make it repsonsive) and I think it’s turning out pretty good so far. I don’t want it to just be this bland though, so I’m gonna add some stuff in the background.
Can’t believe my last devlog was two weeks ago…
Anyway, here’s what I’ve been working on.
It works! Probably… I’m pretty sure it works. At least for my testing. If you guys find any bugs, be sure to tell me.
Sorry that this devlog is pretty short, for a 5 hour devlog. I’m just kinda tired of writing tbh and I can’t wait to ship this.
I made bad apple run in terminal using the cascii rust crate to convert the video to frames, and then drew those frames while clearing the terminal. This is just a joke project so I’m not gonna ship it or anything.
Here’s the code, but it’s pretty bad. I’m by no means the greatest programmer. There’s also a lot of warnings, but I’m too lazy to fix them.
use cascii::{AsciiConverter, VideoOptions, ConversionOptions};
use std::path: :Path;
use std::fs::{self, File};
use std::io::Read;
use terminal_size::{Width, Height, terminal_size};
use crossterm::{
cursor::{Hide, Show, MoveTo},
execute,
terminal::{Clear, ClearType},
};
use std::io::{self, stdout};
use std::thread;
use std::time::Duration;
fn main() -> std::io::Result<()> {
if !Path::new("output_frames").is_dir() {
get_frames();
}
let mut frames: Vec<String> = read_files()?;
play_animation(frames)?;
// println!("{}", frames[0]);
Ok(())
}
fn get_frames() -> Result<(), Box<dyn std::error: :Error>> {
let converter = AsciiConverter::new();
let size = terminal_size();
let mut width: u32 = 80;
let mut height = 0;
if let Some((Width(w), Height(h))) = size {
width = u32::from(w);
} else {
println!("unable to get terminal size");
}
let video_options = VideoOptions {
fps: 30,
start: Some("0".to_string()),
end: Some("10".to_string()),
columns: width,
extract_audio: false,
preprocess_filter: None,
};
let conversion_options = ConversionOptions::default().with_font_ratio(0.2).with_luminance(20).with_columns(width);
converter.convert_video(Path::new("assets/apple.mp4"), Path::new("output_frames"), &video_options, &conversion_options, false)?;
Ok(())
}
fn read_files() -> std::io::Result<Vec<String>> {
let mut files = Vec::new();
for file in fs::read_dir("output_frames/").unwrap() {
files.push(file.unwrap().path().display().to_string());
}
files.sort();
let mut frames: Vec<String> = Vec::new();
for file in files {
let content = fs::read_to_string(file);
let frame = match content {
Ok(val) => val,
Err(err) => format!("error: {}", err),
};
frames.push(frame);
}
Ok(frames)
}
fn play_animation(frames: Vec<String>) -> io::Result<()> {
let frame = 0;
let mut stdout = stdout();
for frame in &frames {
execute!(stdout, Hide, Clear(ClearType::All))?;
execute!(stdout, MoveTo(0, 0));
println!("{}", frame);
thread::sleep(Duration::from_millis(33));
}
execute!(stdout, Show)?;
Ok(())
}
Probably my last devlog before working on saving and exporting T-T
I can’t believe I didn’t add this earlier, but you can now end the game using matu.runtime.stop();
You can now have text labels in your game! I also added scripting implementation, so you can change what the text says during a run.
Reordering nodes was a bit clunky, so I fixed it (turns out there were some bugs in the CSS and the dragging), but now it should be smoother!
This is probably gonna be a shorter devlog, but basically I made scripting a lot easier in my game engine!
Each script node now has a button in the top right of the inspector editor. I just reused the asset previews but with a textarea. This means you can have multiple popouts open at a time while scripting. It also gives you a much wider area to code in, as the inspector is kinda cramped. All updates in the popout update with the inspector textarea. The popouts are also resizable!
Before, if you typed tab while scripting, it would just move focus to another button instead of indenting. However, now when you press tab, 4 spaces are added. You can select multiple lines to indent multiple lines, and press shift + tab to unindent lines.
Now when you type a character like ( or [ automatically types its closing pair and places your cursor in between them. If you type the closing pair again, it will move your cursor to the outside. As well as that, deleting an empty pair will delete both characters.
Now when you go to another line, it will inherit the indentation of the line above it. :)
I really thought that with the introduction of scripting popouts, I would be getting close to finishing the project, but over the past few days, I’ve realized that there are still a lot of stuff I want to implement. First, I want to add a Label node, allowing users to display text easily. Next (and I can’t believe I haven’t implemented this yet), I want to add a way for the user to stop the game in the script, instead of clicking the stop button. I want to fix node reordering, as right now it is a bit clunky, and of course, the final thing for me to do is to add exporting, saving, and loading.
It’s been some time since I have posted a devlog on this project. I got a little sidetracked and spent a few days working with Rust. It’s probably gonna be my next project (flashcard app, I know, how original), but I’ve also spent some time working on scripting!
So scripting was actually easier than I thought to implement. I have a const in my runtime.js file called matuAPI and it has every special function as well as what to do when that function is called. I’ll attach a screenshot of a section below. So now, on to what I have added!
Wow, that was a lot! Unfortunately, as I was writing this devlog, I realized that I forgot to add a function that lets you change which sprite is being displayed in an object as well as changing the audio source asset. How does this always happen to me? Ok, I’ll be back in a bit. I need to add that real quick.
Ok, I have returned! I have added them to the code and the documentation (which is just a markdown file), so we also have
Thanks for reading this devlog! Up next, I plan to add a pop out window for scripting (as the current one is too small) and of course, exporting, loading, and saving (I’m scared, save me)
This is probably going to be my last devlog before I work on scripting. I’ve put it off for too long, and right now, scripting is pretty limited (it has a start, end, input detection, movement, and console log function). I plan to add a LOT more to scripting, like changing sprites, changing opacity, rotation, cloning, deleting, audio, preview, and more. I also plan to open up the script in a window similar to the preview windows, because coding in the inspector sidebar is a bit constricting. Now, on to what I have been working on!
I can’t believe I put this off for so long, but it finally works. You can play audio from the preview window. Though, it’s a bit ugly right now so I plan on changing that later.
Before this, the two sliders were just sliders, and you couldn’t bring it to a specific value. However, there are now inputs on the right side of the sliders, allowing you to see what number the slider is at and allowing you to input your own values.
I actually created two systems for this. So my idea for the node naming system is that unique names are kept separate across node types. That means that I can have an object called Thing and a group called Thing. My first idea of implementing this was to create a Set() for each node type. So I created group_names = new Set(), object_names = new Set(), sprite_names = new Set(), and so forth for each node type. Then we reuse the renaming function from script.js for the assets, but change it to have a switch statement based on the type. However, this was kind of hard, implementing a switch statement everywhere a rename or deletion or any sort of change happened. I replaced it by simpling using the hierarchy_nodes Map, which contains the names and types of each node. With this, we can just pull the type from the map to make sure that names are kept separate across types. It removes the big switch statements that were everywhere and overall keeps the code much cleaner.
Previously, you could technically reorder things because I previously added reparenting, allowing you to move nodes from one parent to another, but it doesn’t allow you to rearrange nodes within a parent. Now, you can, and although its a bit clunky, it does work and I think it looks good.
Alright, thanks for coming to this TED talk. The next devlog will be about a lot of changes that I will make to scripting.
I was too lazy to add all the features other than basic functionality to scripting, so I decided to work on some small bugs and features instead.
For the grid in the center of the screen, the function I used to draw it didn’t close it off on the right side. I can’t believe it took until now to fix it :|
I changed the symbols on the left side of each of the nodes from emojis to letters. I used emojis because I didn’t want to find assets (sorry), but recently I’ve been feeling like the emojis make it feel AI-generated, so I removed those. Also, a bonus of changing them to letters is having the ability to now have the same name be used across different node types, but when you call them in code, you have to do like G:Object or O:Object.
You might’ve seen in my last devlog, something kind of crazy happened with the slider… Anyway, it’s fixed now, so that’s good! I also ended up customizing the slider.
Before this, I didn’t have a way for users to lock dimensions. I’ve implemented two checkboxes into the inspector, dimension lock and proportion lock. Dimension lock takes the current dimensions and keeps that ratio, while proportion lock keeps the original dimension ratio of the original asset.
Basically, there used to be a bug where if you deleted one asset while another asset was open in the inspector, it would close the inspector. It wasn’t harmful or anything, but it was really annoying. This was a pretty simple fix. I just added a check where the name of the asset in the inspector is compared to the name of the asset being deleted.
I don’t know if I mentioned this in a previous devlog yet, but I added a console for errors from scripting to show! It also displays start and end times. However, it was the wrong color for so long and I didn’t notice. It was just because I put the wrong class name in the css…
This was also a bug that was really annoying. When you created a sprite and opened it up in the inspector, when you uploaded a new asset, it wouldn’t show up in the inspector. This was also pretty easy to fix. I just created a helper function, which basically just remakes the asset select options, and then called it every time an asset is changed, like when one is renamed, uploaded, or deleted.
I can’t believe I didn’t add this yet, but I added a new option in the inspector. If you have multiple sprites attached to an object, you can now select which sprite to use.
Recently I’ve been working on getting the scripting working. I’m using normal javascript with some added functions with something I call matuAPI. But I’m not here to show that right now. Rather, I think I should show some of the code of how my project works! Today, I want to talk about how I get multiple preview windows!
Every time someone double clicks an asset in the asset panel (which we can check by adding an addEventListener('dblclick', () => {});), a function called openPreview is called. When we call that function, we check to see if a preview of that file is already opened. We have a Map() called preview_windows, which contains all the previews that are currently open. If a preview for that file is already opened, we can simply just bring the preview back to the center of the screen.
Now, what happens if we don’t have a preview open? Then, we create a new div with the class name preview-window. Inside that div, we will have another div for the preview header (which contains the file name and the word “Preview”) and below that div, an img tag with the file image. If the file type is an image, then we set the src of the img to URL.createObjectURL(file). I haven’t had the time to add audio previews yet though…
What happens next? A function called centerWindow is called. It brings the preview div to a select random few points in the middle of the screen. This prevents all the previews from opening up in the same spot. Next, in order to make it draggable, we call a function called dragElement. It detects when the “handle” (the preview header from above) has a mousedown and mouseup event, using a document.addEventListener('mousemove', elementDrag); and document.addEventListener('mouseup', stopDrag);. In each of these functions, we change the position of the element that is being dragged when the mouse is down. We also call a bringToFront function, which just adds a predetermined number to the element’s z-index.
When the user clicks the close button in the header of the preview, we call the closePreview function, which is pretty simple. It removes the name of the file from the preview_windows map, and just deletes the preview.
There were some bugs while making this; renaming issues while a preview is open, the preview not closing when the asset is deleted, and some other elements that had to be added. But I’m pretty proud of the fact that it works and some QoL features that I added to it, like reopening the preview recentering it.
Thanks for reading this all the way down :) Next devlog, I think I’m going to plan on describing how scripting works!
I mean, you can now create them. Peep the screenshot below! I had to create a new .js file because script.js was getting a little crowded. It was getting a little hard to find stuff
I’m pretty proud of how it works, but I’m planning on improving it later.
I’ve added 5 node types.
Groups are used to group other nodes together. Groups can hold other groups or objects directly under them, but no scripts, audios, or sprites.
Objects are like the main nodes you will use. They are you use audio, sprites, and scripts. They can stand alone or go under a group. As you can see, in the inspector, you can edit the x values, y values, width, height, etc.
These 3 nodes require a parent object. They cannot hold anything. This is how you attach images, sounds, and code to your object!
This devlog is a little short compared to my other ones, but I’m still proud of what I have made! This is probably gonna just end up as a prototype for the final node system. I’m planning on adding the option to lock width and height, letting users drag nodes around and locking them, and reordering nodes. Right now, you can just drag them around from group to group, but you can’t change their order.
After finishing the hierarchy, I’m going to move on to getting the coding part of the game engine working. I’m planning on using Javascript or a modified version of Javascript as matu’s language. Maybe I’ll call it matuscript heh
Next, the obvious choice is to work on simulating the game in the viewport. (I’m scared of this)
Finally, I want to work on save files and exporting. I still don’t know how I’m going to accomplish this (I’m even more scared of this)
Maybe I’ll distract myself by working on the looks of matu first.