Adding Custom Sprites to Bots
Is your feature request related to a problem? Please describe. Currently there are only eight colours of bots, which the bot/client does not select. These limit the visual customization of a bot.
Describe the solution you'd like When the bot connects to the server, it would have a choice to send an image that would replace the colour of the bot. This image would replace the colour for the whole tournament.
Describe alternatives you've considered Allow the bot to chose its own colour would also allow for limited customization of the bot. This could be done by sending a hex code to the server. However, this might cause an issue if two bots pick similar or the same colours.
OK, That is cool. Keep in mind that robots in the viewer must be scaled to the arena and screen size. See "scale" in viewer code. Not all images will scale well. What are the requirements of the image? What can tkinter display? How do we send the image from robot to server to viewer?
Also, we need to make sure this does not conflict with https://github.com/dbakewel/netbots/issues/28.
Regarding tkinter's display capabilities, tkinter can display .gif images using the PhotoImage class and has a function to scale images. There should be a size requirement of the image such as 25 by 25 pixels. I have not figured out what kind of scaling tkinter uses.
OK, lets' assume we go with 25x25 images. There is a limit for the size of a message being sent over the network but I think a 25x25 image should be OK. I am concerned about how we can send the bytes over the network. I suggest a prototype test that loads an image, packs it in network format, unpacks it back to python PhotoImage class, then scales and displays it in a tk window.
In python/pseudocode:
import tkinter as t
import umsgpack
image1 = loadimage(<filename>.gif)
#pack image for sending over network
binary_image = umsgpack.packb(image1)
#unpack image as if it was sent over network
image2 = umsgpack.unpackb(binary_image)
if image1 != image2:
ERROR
opentkwindow()
displayimage(image2)
scaleimage(image2)
rotateimage(image2)
This encodes the image in base64 so it can be transferred, then uses the built in feature of tkinter's PhotoImage class to translate the base64 string to an image.
import tkinter as t
import umsgpack
import base64
tk = t.Tk()
tk.title("Photo")
canvas = t.Canvas(tk, width=500, height=500)
canvas.pack()
with open("test2.gif", "rb") as imageFile:
encode = base64.b64encode(imageFile.read())
image1 = encode
# pack image for sending over network
binary_image = umsgpack.packb(image1)
# unpack image as if it was sent over network
image2 = umsgpack.unpackb(binary_image)
if image1 != image2:
print("Error")
image = t.PhotoImage(data=image1)
scale_w = int(250 / 25)
scale_h = int(250 / 25)
image = image.subsample(scale_w, scale_h)
canvas.create_image((250, 250), image=image)
tk.mainloop()
test image

Good work on this!
The base64 encoding is not actually requited. MesssagePack is capable of packing a python byte array. So you can just read the file data into memory and then pack it. This will be a bit faster because you don't do the base64 encode/decode. It will also send about 20% less data over the network since base64 is larger.
import tkinter as t
import umsgpack
tk = t.Tk()
tk.title("Photo")
canvas = t.Canvas(tk, width=500, height=500)
canvas.pack()
with open("test2.gif", "rb") as imageFile:
image1 = imageFile.read()
# pack image for sending over network
binary_image = umsgpack.packb(image1)
# unpack image as if it was sent over network
image2 = umsgpack.unpackb(binary_image)
if image1 != image2:
print("Error")
image = t.PhotoImage(data=image2)
scale_w = int(250 / 25)
scale_h = int(250 / 25)
image = image.subsample(scale_w, scale_h)
canvas.create_image((250, 250), image=image)
tk.mainloop()
``
Since adding custom images to bots isn't feasible with just tkinter, would we be able to add a feature that would allow bots to customize their colour?
Yes, that would be good. Then during testing the bot you care about would always have the same color.
I suggest:
-
Adding a color field to the botTemplate (see SrvData). This would default to "None". It would be the "preferred color" of the robot. This is only the preferred color and the viewer is allowed to ignore it if it wants. The format would be any valid tk color string: "#ababab", "red", etc...
-
Adding an optional "color" field to the joinRequest message of type "str" between 1 and 16 chars (see optional class field for how to do this). If included it would be copied to the robots color field (which would then be available to the viewer).
-
If the viewer sees that a robot has a color field which is not "None" then the viewer would use it. The viewer should probably validate it's a valid color string otherwise the viewer would through an error.
-
Add a command line option to the viewer "-ignorerobotcolor" which ignores the preferred color field from robots.
This still has the issue of two bots in the viewer which end up with very similar colors but it a robot's preferred color is not close to any of the default viewer colors then it would work.