more diary
@ -1,73 +1,112 @@
|
||||
<!--201001,201112-->
|
||||
<!--221006,220922,221103-->
|
||||
<h1>coroutines in godot engine </h1>
|
||||
september 17, 2020<br>
|
||||
#coroutines #godot #programming<br>
|
||||
august 11, 2022<br>
|
||||
#gamedev #coroutines #godotengine #programming #demo<br>
|
||||
<br>
|
||||
<b>Coroutines</b> are functions that, instead of running to completion, can yield until certain criteria are met. Godot Engine supports coroutines through <a href="https://docs.godotengine.org/en/stable/classes/class_@gdscript.html#class-gdscript-method-yield"><b>yield</b> ( Object object=null, String signal="")</a>, <a href="https://docs.godotengine.org/en/stable/classes/class_gdscriptfunctionstate.html#class-gdscriptfunctionstate-method-resume"><b>resume</b></a>, and the <a href="https://docs.godotengine.org/en/stable/classes/class_gdscriptfunctionstate.html"><b>GDScriptFunctionState</b></a> object.<br>
|
||||
<p>Demonstrating coroutines in Godot Engine with a simple application. <br></p>
|
||||
<img src="/static/img/ent/stoplight_screenshot.png" alt="(screenshot: a simple tech demo with a stoplight and a walk button.)"><br>
|
||||
<br>
|
||||
<h2>why use a coroutine? </h2>
|
||||
<h2>defining coroutines </h2>
|
||||
<p>Coroutines are functions that, instead of running to completion, yield until certain criteria are met. Godot Engine supports coroutines through <a href="https://docs.godotengine.org/en/stable/classes/class_@gdscript.html#class-gdscript-method-yield">yield()</a>, <a href="https://docs.godotengine.org/en/stable/classes/class_gdscriptfunctionstate.html#class-gdscriptfunctionstate-method-resume">resume()</a>, and the <a href="https://docs.godotengine.org/en/stable/classes/class_gdscriptfunctionstate.html">GDScriptFunctionState</a> object. <br></p>
|
||||
<br>
|
||||
Coroutines allow for scripted game scenarios that respond dynamically to the player and the changing game world. They let you bounce between functions, step-by-step, and respond to interruptions. This means functions can be automatically called at the completion of other functions, animations, player actions, in-game events, or timers. Add in interruptions and conditionals, and you have a tool for building a responsive game world. <br>
|
||||
<h2>why use a coroutine? </h2>
|
||||
<p>Coroutines allow for scripted game scenarios that respond dynamically to the player and the changing game world. They let you bounce between functions, step-by-step, and respond to interruptions. This means functions can be automatically called at the completion of other functions, animations, player actions, in-game events, or timers. Add in interruptions and conditionals, and you have a tool for building a responsive game world. <br></p>
|
||||
<br>
|
||||
<h2>stoplight example </h2>
|
||||
<p>As a simple demonstration, I made a stoplight. Follow along with my code on <a href="https://gitlab.com/chimchooree/stoplight">GitLab</a>. <br></p>
|
||||
<br>
|
||||
As a basic example of coroutines in Godot Engine, I made a stoplight. Follow along with my code on <a href="https://gitlab.com/chimchooree/stoplight">GitLab</a>. <br>
|
||||
<br>
|
||||
In my example, the light changes every few seconds, going from green, yellow, then finally red. The light changes immediately if the Walk Button is pressed. This project demonstrates methods that can wait, resume, and be affected through player action. <br>
|
||||
<p>The light changes every few seconds, going from green, yellow, then red. The light changes immediately if the walk button is pressed. This demonstrates that methods can wait for criteria (a timed duration in this case) to be met before resuming, and they can be influenced by player action. <br></p>
|
||||
<br>
|
||||
<center>
|
||||
<a target="_blank" href="/static/img/ent/stoplight_demonstration.gif">
|
||||
<img src="/static/img/ent/stoplight_demonstration.gif" alt="(gif: demonstration)" width="500" height="281.67">
|
||||
</a><br>
|
||||
</center>
|
||||
<br>
|
||||
<h2>how does it work? </h2>
|
||||
<br>
|
||||
<h2>how is it written? </h2>
|
||||
<h3>node hierarchy </h3>
|
||||
<br><center>
|
||||
<img src="/static/img/ent/stoplight_nodehierarchy.png" alt="(image: node hierarchy - Root is a node named Main. It's children are TextureRect BG, AnimatedSprite Stoplight, Sprite WalkButton, and a Label. Stoplight's child is a Sprite. WalkButton's child is a TextureButton.)"><br>
|
||||
</center>
|
||||
<br>
|
||||
I have a TextureRect background, an AnimatedSprite stoplight, a Sprite walk button with a TextureButton, and a label for displaying a timer. Since this is a simple example, most of the code is attached to the root. It's better to have code closer to where it's being used and to watch your separation of concerns in real projects, though. <br>
|
||||
<img src="/static/img/ent/stoplight_nodehierarchy.png" alt="(screenshot: node hierarchy in the editor. Root is a node named Main. It's children are TextureRect BG, AnimatedSprite Stoplight, Sprite WalkButton, and a Label. Stoplight's child is a Sprite. WalkButton's child is a TextureButton.)"><br>
|
||||
<p>I have a TextureRect background, an AnimatedSprite stoplight, a Sprite walk button with a TextureButton, and a label for displaying a timer. Most of the code is attached to the root. It's better to have code closer to where it's being used and to mind your separation of concerns in real projects, though. <br></p>
|
||||
<br>
|
||||
<h3>animation</h3>
|
||||
<br>
|
||||
<center>
|
||||
<img src="/static/img/ent/stoplight_animationframes.png" alt="(image: the AnimatedSprite Stoplight has 4 animations - default (which is no light), green, red, and yellow.)"><br>
|
||||
</center><br>
|
||||
The light is changed by setting its animation to one of these options. Each is one-frame - just the stoplight with the one or none of the lights colored in. <br>
|
||||
<p>The light is changed by setting its animation to one of these options. Each is one-frame - just the stoplight with the one or none of the lights colored in. <br></p>
|
||||
<h3>the code </h3>
|
||||
<br>
|
||||
This project has two scripts: Main.gd, which is attached to the root node, and Label.gd, which is attached to the Label. <br>
|
||||
<br>
|
||||
<b>Main.gd</b> - code available on <a href="https://gitlab.com/chimchooree/stoplight/-/blob/master/Main.gd">GitLab</a><br>
|
||||
<center>
|
||||
<img src="/static/img/ent/stoplight_main.png" alt="(image: Main script.)"><br>
|
||||
</center>
|
||||
<br>
|
||||
<b>Label.gd</b> - code available on <a href="https://gitlab.com/chimchooree/stoplight/-/blob/master/Label.gd">GitLab</a><br>
|
||||
<center>
|
||||
<img src="/static/img/ent/stoplight_label.png" alt="(image: Label script.)"><br>
|
||||
</center>
|
||||
<h4>Main.gd - available on <a href="https://gitlab.com/chimchooree/stoplight/-/blob/master/Main.gd">GitLab</a></h4>
|
||||
<pre><code>extends Node
|
||||
|
||||
onready var stoplight = $Stoplight
|
||||
|
||||
func _ready():
|
||||
stoplight.play()
|
||||
|
||||
var result = wait(5, 'green')
|
||||
$WalkButton/TextureButton.connect('pressed', result, 'resume',
|
||||
['interrupted on green'], CONNECT_ONESHOT)
|
||||
yield(result, 'completed')
|
||||
|
||||
result = wait(5, 'yellow')
|
||||
$WalkButton/TextureButton.connect('pressed', result, 'resume',
|
||||
['interrupted on yellow'], CONNECT_ONESHOT)
|
||||
yield(result, 'completed')
|
||||
|
||||
result = wait(5, 'red')
|
||||
$WalkButton/TextureButton.connect('pressed', result, 'resume',
|
||||
['interrupted on red'], CONNECT_ONESHOT)
|
||||
yield(result, 'completed')
|
||||
|
||||
func wait(time, color):
|
||||
print('waiting for: ' + color)
|
||||
var result = yield(get_tree().create_timer(time), 'timeout')
|
||||
if result:
|
||||
print(result)
|
||||
stoplight.animation = color
|
||||
print('done: ' + color)
|
||||
|
||||
func _on_completed():
|
||||
print('completed')
|
||||
|
||||
func _on_WalkButton_gui_input(event):
|
||||
if event is InputEventMouseButton and event.pressed:
|
||||
print ("Walk Button not functioning.")</code></pre><br>
|
||||
<br>
|
||||
<h4>Label.gd - available on <a href="https://gitlab.com/chimchooree/stoplight/-/blob/master/Label.gd">GitLab</a></h4>
|
||||
<pre><code>extends Label
|
||||
var time_start = 0
|
||||
var time_now = 0
|
||||
|
||||
func _ready():
|
||||
time_start = OS.get_unix_time()
|
||||
set_process(true)
|
||||
|
||||
func _process(delta):
|
||||
time_now = OS.get_unix_time()
|
||||
var elapsed = time_now - time_start
|
||||
var minutes = elapsed / 60
|
||||
var seconds = elapsed % 60
|
||||
var str_elapsed = "%02d" % [seconds]
|
||||
text = str(str_elapsed)</code></pre><br>
|
||||
<br>
|
||||
<h3>how the code works </h3>
|
||||
<br>
|
||||
At <code>_ready()</code>, <code>wait()</code> is assigned to the GDScriptFunctionState <code>result</code> and is called for the first color, green. <code>_ready()</code> yields until the given function <code>wait()</code> is completed. <br>
|
||||
<h2>how does it work? </h2>
|
||||
<p>At <span class="code">_ready()</span>, <span class="code">wait()</span> is assigned to the GDScriptFunctionState <span class="code">result</span> and is called for the first color, green. <span class="code">_ready()</span> yields until <span class="code">wait()</span> is completed. <br></p>
|
||||
<br>
|
||||
The wait method yields for the given amount of seconds then sets the stoplight to the given color. <br>
|
||||
<p>The wait method yields for the given amount of seconds then sets the stoplight to the given color. <br></p>
|
||||
<br>
|
||||
At <code>wait()</code>'s completion, <code>_ready()</code> calls <code>wait()</code> for yellow, then red. Each is called one at a time, waiting for the color to complete before moving on. <br>
|
||||
<p>At <span class="code">wait()</span>'s completion, <span class="code">_ready()</span> calls <span class="code">wait()</span> for yellow, then red. Each is called one at a time, waiting for the color to complete before moving on. <br></p>
|
||||
<br>
|
||||
<h3>interrupting the stoplight </h3>
|
||||
<p>The Wait Button interrupts the wait times between colors. Before <span class="code">_ready()</span> yields, it connects the <span class="code">'pressed'</span> signal on the Wait Button. <br></p>
|
||||
<br>
|
||||
<p>If the Wait Button is clicked during <span class="code">wait()</span>'s yield, the GDScriptFunctionState <span class="code">result</span> resumes immediately, ignoring <span class="code">wait()</span>'s yield timer. This time, <span class="code">result</span> has a string arg <span class="code">"interrupted on green,"</span> so it will print the result, change the stoplight's color, then print <span class="code">"done: green."</span> The <span class="code">wait</span> method is complete, so <span class="code">_ready()</span> resumes and calls <span class="code">wait()</span> for the next color. <br></p>
|
||||
<br>
|
||||
The Wait Button interrupts the wait times between colors. Before <code>_ready()</code> yields, it connects the <code>'pressed'</code> signal on the Wait Button. <br>
|
||||
If the Wait Button is clicked during <code>wait()</code>'s yield, the GDScriptFunctionState <code>result</code> resumes immediately, ignoring <code>wait()</code>'s yield timer. This time, <code>result</code> has a string arg <code>'interrupted on green'</code>, so it will print the result, change the stoplight's color, then print <code>'done: green'</code>. The <code>wait</code> method is complete, so <code>_ready()</code> resumes and calls <code>wait()</code> for the next color. <br>
|
||||
<h2>play it yourself </h2>
|
||||
<iframe frameborder="0" src="https://itch.io/embed/1643944?dark=true" width="552" height="167"><a href="https://chimchooree.itch.io/stoplight">stoplight by chimchooree</a></iframe><br>
|
||||
<br>
|
||||
<h2>applications </h2>
|
||||
<p>The outcomes in this example can be swapped out with anything. I use coroutines in Blessfrey's skills to manage the flow of phases from activation, different phases of effects, cooldown, and interactions with any counters. I also use it in the basic weapon attack so the character continuously swings at the rate of his attack speed until he cancels, uses a skill, or moves. It could also be used for something like cars that stop and honk when the player walks in front of them then drive off once the path is clear. Anything influenced by other entities is a good coroutine candidate. <br></p>
|
||||
<br>
|
||||
The outcomes in this example can be swapped out with anything. I use coroutines in Blessfrey's skills to manage the flow of phases from activation, different phases of effects, cooldown, and interactions with any counters. I also use it in the basic weapon attack so the character continuously swings at the rate of his attack speed until he cancels, uses a skill, or moves. It could also be used for something like cars that stop and honk when the player walks in front of them then drive off once the path is clear. <br>
|
||||
<p>Coroutines enable practical ways to improve the flow and interactivity of games, so practice the concept a lot! <br></p>
|
||||
<br>
|
||||
Coroutines enable lots of practical ways to improve the flow and interactivity of your game, so just keep experimenting. <br>
|
||||
<br>
|
||||
Last updated June 8, 2021 <br>
|
||||
Last updated July 31, 2022 <br>
|
||||
<br>
|
||||
|
@ -1,40 +0,0 @@
|
||||
<!--200917,201112-->
|
||||
<h1>inventory as a system diagram </h1>
|
||||
january 7, 2021<br>
|
||||
#design #systemdiagram #gamemechanics<br>
|
||||
<br>
|
||||
<b>System diagrams</b> illustrate how components interact within a system. It saves so much headache to step back and plan with a system diagram before jumping into code. <br>
|
||||
<br>
|
||||
<br>
|
||||
<h2>stop + plan before coding </h2><br>
|
||||
I want to move blessfrey's inventory into an app on the player character's smartphone. Before, it was displayed in a random pop-up window. It was poorly planned, so the programmatic inventory and the UI were too tightly coupled to easily pop into the phone screen. Instead of wrestling, it's easier to start over and actually plan before I code this time. <br>
|
||||
<br>
|
||||
<br>
|
||||
<h2>list out your nouns </h2><br>
|
||||
A simple way to start thinking about a system is to list out its nouns + verbs. Jot down the entities that interact with your system. <br>
|
||||
<br>
|
||||
<center><img src="/static/img/ent/systemdiagram_inventory.jpeg" alt="(image: system diagram for inventory)" width="500" height="250"></center> <br>
|
||||
<br>
|
||||
For blessfrey's inventory, that's the inventory (programmatic), the player character, the inventory app (UI), and base items. The inventory app is related to the smartphone and inventory items. Items are related to floor items, which are related to rooms. <br>
|
||||
<br>
|
||||
(blessfrey has three different kinds of items. <br>
|
||||
<br><ul>
|
||||
<li><b>Base Item</b>: holds all the data regardless of how the item is currently expressed </li>
|
||||
<li><b>Floor Item</b>: sits on the ground and gets picked up by characters. </li>
|
||||
<li><b>Inventory Item</b>: displayed in inventories, store windows, containers, etc </li>
|
||||
</ul><br>
|
||||
Floor + Inventory Items hold a base item inside them that gets popped out and traded around as the item gets expressed in different forms.) </br>
|
||||
<br>
|
||||
<h2>connect your nouns with verbs </h2><br>
|
||||
I wrote the entities in pink and moved them around until the placement was decently readable. Then I connected the concepts with arrows. These arrows represent the verbs, which I explicitly labeled in yellow. <br>
|
||||
<br>
|
||||
The flow of these arrows can be very important. If you are modeling AI, for instance, no matter what path the program takes, there shouldn't be dead-ends. Seeing mistakes like that is easier with a diagram than lines of code. Otherwise, the flow is always generally useful for figuring out which methods are needed for each class and how they connect. <br>
|
||||
<br>
|
||||
<br>
|
||||
<h2>write the code </h2><br>
|
||||
At this point, coding is a bit easier now that it's in some ways a transcription of the diagram. The entities are data (classes or objects), and the arrows are logic (methods). <br>
|
||||
<br>
|
||||
<br>
|
||||
<h2>conclusion </h2><br>
|
||||
Your diagram doesn't have to be fancy or formal to get the point across to yourself, and it just takes a minute to save all the headache later. Don't skip this step or you'll have to rewrite the inventory system just to display it in a different window. <br>
|
||||
<br>
|
@ -1,33 +0,0 @@
|
||||
<!--220825,220728-->
|
||||
<h1>no-legs the cat </h1>
|
||||
august 11, 2022<br>
|
||||
#game #no-legs-the-cat #godotengine #release<br>
|
||||
<br>
|
||||
<p><b>No-Legs the Cat</b> is a 2D maze game featuring Poltics Cat! Help him find his legs! He can't move by himself, so scoot him around with the arrow keys. Don't forget to feed him all three breakfasts or he'll starve!! Go play it <a href="http://127.0.0.1:9001/demo">here</a> or on <a href="https://chimchooree.itch.io/legless-the-cat">itch.io</a>. <br></p>
|
||||
<img src="/static/img/ent/everythingscomingtogether_no-legsthecat.png" alt="(No-Legs the Cat screenshot: Poltics Cat finds a bowl of breakfast in the maze)"><br>
|
||||
<br>
|
||||
<p>It's not much - just a demonstration to myself that I can export a game and embed HTML5 applications here. It runs pretty well locally, so we'll see how well it runs on the live server in my playtesters' browsers. I'm getting close to releasing the first minor demo for Blessfrey, so I'd really rather iron out all the kinks with a short and simple project first, you know? Dreading the day I have to handle serialization in the browser. <br></p>
|
||||
<br>
|
||||
<p>Have fun! More games coming soon. <br></p>
|
||||
<br>
|
||||
<h2>faq</h2>
|
||||
<h3>who is poltics cat? </h3>
|
||||
<img src="/static/img/ent/nolegsthecat_polticscat.png" alt="(photo: My pet orange kitty with an empty look on his face and legs tucked entirely under his body. He looks like a loaf of bread.)"><br>
|
||||
<p>My kitty! <br></p>
|
||||
<br>
|
||||
<p>It's just an inside joke. There was a guy who kept interrupting the political discussion channel by posting his pet, so I tried to imitate him with my own politics cat. Only I made a typo, and it stuck. <br></p>
|
||||
<br>
|
||||
<h3>where'd his legs go? </h3>
|
||||
<p>Aren't cats cute when they sit like that? It's called "loafing." Kitty's so fluffy that his legs become totally hidden when he does that, and he just stares helplessly at us when we start heckling him. <br></p>
|
||||
<br>
|
||||
<p>It looks like his legs are missing for real this time, though. He couldn't have gotten far without them, so they have to be somewhere in the maze. <br></p>
|
||||
<br>
|
||||
<h3>isn't one breakfast enough? </h3>
|
||||
<p>I dunno, my cat is weird. He wants us to give him breakfast first thing in the morning, then again when we eat our own breakfast. It's still so early, it's like he eats two breakfasts. He doesn't care about food for the rest of the day. It's like the concept of lunch and dinner are completely foreign to him. He's a dishonest little guy, though, so he'll come to both my husband and me separately to beg for "second" breakfast. We're usually too smart for him, but I'd be lying if he hasn't bamboozled us into <i>three</i> whole breakfasts before. <br></p>
|
||||
<br>
|
||||
<h3>when are godot devs going to stop making everything out of the godot head? </h3>
|
||||
<p>Probably never. <br></p>
|
||||
<br>
|
||||
<br>
|
||||
Last updated July 21, 2022 <br>
|
||||
<br>
|
@ -1,52 +0,0 @@
|
||||
<!--220728,220811-->
|
||||
<h1>artfight 2022: team bloom </h1>
|
||||
august 25, 2022<br>
|
||||
#personal #artfight #art <br>
|
||||
<br>
|
||||
<h2>what's artfight? </h2>
|
||||
<b>Artfight</b> is an online art game every July. You post profiles for your original characters then go off browsing to find someone else's character to draw. Everyone's sorted into teams and each "attack" or drawing is valued for a certain number of points, so one side wins at the end of the event. You don't get anything for winning, and people game the system so bad that it's hard to even care about the competitive side of the site. <br></p>
|
||||
<br>
|
||||
<p>It's more fun to approach the game with your own personal challenge. I know a lot of people try a new style or technique during this month, like my friend trying single-layer digital paintings. My personal approach this year is thematic. I'm drawing cute couples! I don't usually draw boys, so it's a good compromise if his girlfriend will be in the frame, too. Also, more characters per attack = more points! <br></p>
|
||||
<br>
|
||||
It's a huge community, so at least a few people are bound to draw your characters, which is such a fun surprise when it happens. Some people like to make art in return for people who attack them, too, as thanks. Their discord is probably the best place to get attention, but it's intimidatingly fast for me. <br>
|
||||
<br>
|
||||
<h2>my attacks </h2>
|
||||
<p>These are the characters I drew. <br></p>
|
||||
<img src="/static/img/ent/AF22_Abbey_Lune_Arcon_SanoAmaterasu_goddess.png" alt="(pixelart: Sano Amaterasu in a lupine field, playing her harp.)"><br>
|
||||
<img src="/static/img/ent/AF22_Abbey_Lune_Arcon_SanoAmaterasu_goddess_big.png" alt="(pixelart: same but larger size)"><br>
|
||||
My pixelart of Lune_Archon's Sano Amaterasu, a horned goddess with her harp. (75x75px, 45 colors) <br>
|
||||
<br>
|
||||
<img src="/static/img/ent/AF22_Abbey_Skye_0723_Aisu+LunaStarleaf.png" alt="(pixelart: Aisu, a blue winged man in a sweater standing behind his seated blonde wife with a pink shirt.)"><br>
|
||||
<img src="/static/img/ent/AF22_Abbey_Skye_0723_Aisu+LunaStarleaf_big.png" alt="(pixelart: same but larger size.)"><br>
|
||||
My pixelart of Skye_0723's Aisu and Luna Starleaf, a winged warrior and his druid wife who can turn into a pink unicorn. (163x157px, 79 colors) <br>
|
||||
<br>
|
||||
<img src="/static/img/ent/AF22_Abbey_EmmArrGus_Brody+Char.png" alt="(pixelart: Brody, a scruffy fisherman sitting on a bench with his anthro shark girlfriend Char.)"><br>
|
||||
<img src="/static/img/ent/AF22_Abbey_EmmArrGus_Brody+Char_big.png" alt="(pixelart: same but larger size.)"><br>
|
||||
My pixelart of EmmArrGus's fisherman Brody and his shark girlfriend Char. (150x123px, 54 colors) <br>
|
||||
<br>
|
||||
<img src="/static/img/ent/AF22_Abbey_BlackReshiram_Lilac_dragongirl.png" alt="(pixelart: a fluffy pink dragon girl named Lilac.)"><br>
|
||||
<img src="/static/img/ent/AF22_Abbey_BlackReshiram_Lilac_dragongirl_big.png" alt="(pixelart: same but larger size.)"><br>
|
||||
My pixelart of BlackReshiram's Lilac, a fluffy pink dragon girl. (51 colors) <br>
|
||||
<br>
|
||||
<h2>my defenses </h2>
|
||||
<p>These are my characters, drawn by other people. <br></p>
|
||||
<img src="/static/img/ent/AF22_CawfeeCakes_Abbey_Rune_sadlittleguy.png" alt="(image: a bust of a sad Rune in a dark hoodie)"><br>
|
||||
CawfeeCakes's bust of Rune. He's so sad, he's droopy! <br>
|
||||
<br>
|
||||
<img src="/static/img/ent/AF22_Lune_Archon_Abbey_TessaSkyeStMartin.jpeg" alt="(image: Tessa with a vampy lip!)"><br>
|
||||
Lune_Archon's bust of Tessa with a vampy lip! <br>
|
||||
<br>
|
||||
<img src="/static/img/ent/AF22_BlackReshiram_Abbey_floofdrago.png" alt="(image: Abbey in an adorable style!)"><br>
|
||||
BlackReshiram's illustration of Abbey in an adorable style! <br>
|
||||
<br>
|
||||
<br>
|
||||
<h2>do you recommend this game? </h2>
|
||||
<p>It's one of the more fun art communities these days. A lot of communities have turned completely business-minded or are littered with off-topic content. Artfight, however, is necessarily interactive and collaborative, more like how art communities felt in the 10s. I try to play every year to motivate myself to draw more, especially things outside my comfort zone of cute girls. <br></p>
|
||||
<br>
|
||||
<p>This community has a lot of overlap with Toyhouse and Tumblr, though, so don't be surprised if you keep running into massive text walls of character permissions and "original character donut steal" warnings. There's also a wide range of ability. I see everything from grainy photos of lined paper to Tearzah copycat artists to university illustration students. It feels like old DeviantART in that respect. <br></p>
|
||||
<br>
|
||||
<p>You should know that the servers reliably crash during the first week of July every year, so if you do want to play, prepare during June. Upload a few characters and their reference pictures, find targets you'd like to draw, and save their usernames and reference pictures. Even if the servers crash, you've got everything you need for a few days. <br></p>
|
||||
<br>
|
||||
<br>
|
||||
Last updated June 4, 2022 <br>
|
||||
<br>
|
After Width: | Height: | Size: 2.0 MiB |
After Width: | Height: | Size: 9.5 KiB |
After Width: | Height: | Size: 281 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 149 KiB |
After Width: | Height: | Size: 8.9 KiB |
After Width: | Height: | Size: 122 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 202 KiB |